OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
MoleculeStamp.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44#include "types/MoleculeStamp.hpp"
45
46#include <algorithm>
47#include <functional>
48#include <iostream>
49#include <sstream>
50#include <tuple>
51
52#include "utils/MemoryUtils.hpp"
53
54using namespace std;
55
56namespace OpenMD {
57
58 template<class ContainerType>
59 bool hasDuplicateElement(const ContainerType& cont) {
60 ContainerType tmp = cont;
61 std::sort(tmp.begin(), tmp.end());
62 tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
63 return tmp.size() != cont.size();
64 }
65
66 MoleculeStamp::MoleculeStamp() : nintegrable_(0) {
67 DefineParameter(Name, "name");
68 DefineOptionalParameter(ConstrainTotalCharge, "constrainTotalCharge");
69
70 deprecatedKeywords_.insert("nAtoms");
71 deprecatedKeywords_.insert("nBonds");
72 deprecatedKeywords_.insert("nBends");
73 deprecatedKeywords_.insert("nTorsions");
74 deprecatedKeywords_.insert("nInversions");
75 deprecatedKeywords_.insert("nRigidBodies");
76 deprecatedKeywords_.insert("nCutoffGroups");
77 }
78
79 MoleculeStamp::~MoleculeStamp() {
80 Utils::deletePointers(atomStamps_);
81 Utils::deletePointers(bondStamps_);
82 Utils::deletePointers(bendStamps_);
83 Utils::deletePointers(torsionStamps_);
84 Utils::deletePointers(inversionStamps_);
85 Utils::deletePointers(rigidBodyStamps_);
86 Utils::deletePointers(cutoffGroupStamps_);
87 Utils::deletePointers(fragmentStamps_);
88 Utils::deletePointers(constraintStamps_);
89 }
90
91 bool MoleculeStamp::addAtomStamp(AtomStamp* atom) {
92 bool ret = addIndexSensitiveStamp(atomStamps_, atom);
93 if (!ret) {
94 std::ostringstream oss;
95 oss << "Error in Molecule " << getName()
96 << ": multiple atoms have the same indices" << atom->getIndex()
97 << "\n";
98 throw OpenMDException(oss.str());
99 }
100 return ret;
101 }
102
103 bool MoleculeStamp::addBondStamp(BondStamp* bond) {
104 bondStamps_.push_back(bond);
105 return true;
106 }
107
108 bool MoleculeStamp::addBendStamp(BendStamp* bend) {
109 bendStamps_.push_back(bend);
110 return true;
111 }
112
113 bool MoleculeStamp::addTorsionStamp(TorsionStamp* torsion) {
114 torsionStamps_.push_back(torsion);
115 return true;
116 }
117
118 bool MoleculeStamp::addInversionStamp(InversionStamp* inversion) {
119 inversionStamps_.push_back(inversion);
120 return true;
121 }
122
123 bool MoleculeStamp::addRigidBodyStamp(RigidBodyStamp* rigidbody) {
124 bool ret = addIndexSensitiveStamp(rigidBodyStamps_, rigidbody);
125 if (!ret) {
126 std::ostringstream oss;
127 oss << "Error in Molecule " << getName()
128 << ": multiple rigidbodies have the same indices: "
129 << rigidbody->getIndex() << "\n";
130 throw OpenMDException(oss.str());
131 }
132 return ret;
133 }
134
135 bool MoleculeStamp::addCutoffGroupStamp(CutoffGroupStamp* cutoffgroup) {
136 cutoffGroupStamps_.push_back(cutoffgroup);
137 return true;
138 }
139
140 bool MoleculeStamp::addFragmentStamp(FragmentStamp* fragment) {
141 return addIndexSensitiveStamp(fragmentStamps_, fragment);
142 }
143
144 bool MoleculeStamp::addConstraintStamp(ConstraintStamp* constraint) {
145 constraintStamps_.push_back(constraint);
146 return true;
147 }
148
149 void MoleculeStamp::validate() {
150 DataHolder::validate();
151
152 atom2Rigidbody.resize(getNAtoms());
153
154 // A negative number means the atom is a free atom, and does not
155 // belong to rigidbody. Every element in atom2Rigidbody has unique
156 // negative number at the very beginning
157
158 for (unsigned int i = 0; i < atom2Rigidbody.size(); ++i) {
159 atom2Rigidbody[i] = -1 - int(i);
160 }
161 for (std::size_t i = 0; i < getNRigidBodies(); ++i) {
162 RigidBodyStamp* rbStamp = getRigidBodyStamp(i);
163 std::vector<int> members = rbStamp->getMembers();
164 for (std::vector<int>::iterator j = members.begin(); j != members.end();
165 ++j) {
166 atom2Rigidbody[*j] = i;
167 }
168 }
169
170 checkAtoms();
171 checkBonds();
172 fillBondInfo();
173 checkBends();
174 checkTorsions();
175 checkInversions();
176 checkRigidBodies();
177 checkCutoffGroups();
178 checkFragments();
179 checkConstraints();
180
181 size_t nrigidAtoms = 0;
182 for (std::size_t i = 0; i < getNRigidBodies(); ++i) {
183 RigidBodyStamp* rbStamp = getRigidBodyStamp(i);
184 nrigidAtoms += rbStamp->getNMembers();
185 }
186 nintegrable_ = getNAtoms() + getNRigidBodies() - nrigidAtoms;
187 }
188
189 void MoleculeStamp::checkAtoms() {
190 std::vector<AtomStamp*>::iterator ai = std::find(
191 atomStamps_.begin(), atomStamps_.end(), static_cast<AtomStamp*>(NULL));
192 if (ai != atomStamps_.end()) {
193 std::ostringstream oss;
194 oss << "Error in Molecule " << getName() << ": atom["
195 << ai - atomStamps_.begin() << "] is missing\n";
196 throw OpenMDException(oss.str());
197 }
198 }
199
200 void MoleculeStamp::checkBonds() {
201 std::ostringstream oss;
202 // make sure index is not out of range
203 int natoms = static_cast<int>(getNAtoms());
204 for (std::size_t i = 0; i < getNBonds(); ++i) {
205 BondStamp* bondStamp = getBondStamp(i);
206 if (bondStamp->getA() > natoms - 1 || bondStamp->getA() < 0 ||
207 bondStamp->getB() > natoms - 1 || bondStamp->getB() < 0 ||
208 bondStamp->getA() == bondStamp->getB()) {
209 oss << "Error in Molecule " << getName() << ": bond("
210 << bondStamp->getA() << ", " << bondStamp->getB()
211 << ") is invalid\n";
212 throw OpenMDException(oss.str());
213 }
214 }
215
216 // make sure bonds are unique
217 std::set<std::pair<int, int>> allBonds;
218 for (std::size_t i = 0; i < getNBonds(); ++i) {
219 BondStamp* bondStamp = getBondStamp(i);
220 std::pair<int, int> bondPair(bondStamp->getA(), bondStamp->getB());
221 // make sure bondPair.first is always less than or equal to
222 // bondPair.third
223 if (bondPair.first > bondPair.second) {
224 std::swap(bondPair.first, bondPair.second);
225 }
226
227 std::set<std::pair<int, int>>::iterator iter = allBonds.find(bondPair);
228 if (iter != allBonds.end()) {
229 oss << "Error in Molecule " << getName() << ": "
230 << "bond(" << iter->first << ", " << iter->second
231 << ") appears multiple times\n";
232 throw OpenMDException(oss.str());
233 } else {
234 allBonds.insert(bondPair);
235 }
236 }
237
238 // make sure atoms belong to same rigidbody do not bond to each other
239 for (std::size_t i = 0; i < getNBonds(); ++i) {
240 BondStamp* bondStamp = getBondStamp(i);
241 if (atom2Rigidbody[bondStamp->getA()] ==
242 atom2Rigidbody[bondStamp->getB()]) {
243 oss << "Error in Molecule " << getName() << ": "
244 << "bond(" << bondStamp->getA() << ", " << bondStamp->getB()
245 << ") belong to same rigidbody "
246 << atom2Rigidbody[bondStamp->getA()] << "\n";
247 throw OpenMDException(oss.str());
248 }
249 }
250 }
251
252 void MoleculeStamp::checkBends() {
253 std::ostringstream oss;
254 for (std::size_t i = 0; i < getNBends(); ++i) {
255 BendStamp* bendStamp = getBendStamp(i);
256 std::vector<int> bendAtoms = bendStamp->getMembers();
257 std::vector<int>::iterator j =
258 std::find_if(bendAtoms.begin(), bendAtoms.end(),
259 std::bind(std::greater<int>(), std::placeholders::_1,
260 getNAtoms() - 1));
261 std::vector<int>::iterator k =
262 std::find_if(bendAtoms.begin(), bendAtoms.end(),
263 std::bind(std::less<int>(), std::placeholders::_1, 0));
264
265 if (j != bendAtoms.end() || k != bendAtoms.end()) {
266 oss << "Error in Molecule " << getName() << " : atoms of bend"
267 << containerToString(bendAtoms) << " have invalid indices\n";
268 throw OpenMDException(oss.str());
269 }
270
271 if (hasDuplicateElement(bendAtoms)) {
272 oss << "Error in Molecule " << getName() << " : atoms of bend"
273 << containerToString(bendAtoms) << " have duplicated indices\n";
274 throw OpenMDException(oss.str());
275 }
276
277 if (bendAtoms.size() == 2) {
278 if (!bendStamp->haveGhostVectorSource()) {
279 oss << "Error in Molecule " << getName()
280 << ": ghostVectorSouce is missing\n";
281 throw OpenMDException(oss.str());
282 } else {
283 std::size_t ghostIndex = bendStamp->getGhostVectorSource();
284 if (ghostIndex < getNAtoms()) {
285 if (std::find(bendAtoms.begin(), bendAtoms.end(), ghostIndex) ==
286 bendAtoms.end()) {
287 oss << "Error in Molecule " << getName() << ": ghostVectorSouce "
288 << ghostIndex << "is invalid\n";
289 throw OpenMDException(oss.str());
290 }
291 if (!getAtomStamp(ghostIndex)->haveOrientation()) {
292 oss << "Error in Molecule " << getName()
293 << ": ghost atom must be a directional atom\n";
294 throw OpenMDException(oss.str());
295 }
296 } else {
297 oss << "Error in Molecule " << getName() << ": ghostVectorSource "
298 << ghostIndex << " is invalid\n";
299 throw OpenMDException(oss.str());
300 }
301 }
302 } else if (bendAtoms.size() == 3 && bendStamp->haveGhostVectorSource()) {
303 oss << "Error in Molecule " << getName()
304 << ": normal bend should not have ghostVectorSouce\n";
305 throw OpenMDException(oss.str());
306 }
307 }
308
309 for (std::size_t i = 0; i < getNBends(); ++i) {
310 BendStamp* bendStamp = getBendStamp(i);
311 std::vector<int> bendAtoms = bendStamp->getMembers();
312 std::vector<int> rigidSet(getNRigidBodies(), 0);
313 std::vector<int>::iterator j;
314 for (j = bendAtoms.begin(); j != bendAtoms.end(); ++j) {
315 int rigidbodyIndex = atom2Rigidbody[*j];
316 if (rigidbodyIndex >= 0) {
317 ++rigidSet[rigidbodyIndex];
318 if (rigidSet[rigidbodyIndex] > 2) {
319 oss << "Error in Molecule " << getName() << ": bend"
320 << containerToString(bendAtoms)
321 << "has three atoms on the same rigid body\n";
322 throw OpenMDException(oss.str());
323 }
324 }
325 }
326 }
327
328 std::set<std::tuple<int, int, int>> allBends;
329 std::set<std::tuple<int, int, int>>::iterator iter;
330
331 for (std::size_t i = 0; i < getNBends(); ++i) {
332 BendStamp* bendStamp = getBendStamp(i);
333 std::vector<int> bend = bendStamp->getMembers();
334 if (bend.size() == 2) {
335 // in case we have two ghost bend. For example,
336 // bend {
337 // members (0, 1);
338 // ghostVectorSource = 0;
339 // }
340 // and
341 // bend {
342 // members (0, 1);
343 // ghostVectorSource = 0;
344 // }
345 // In order to distinguish them. we expand them to Tuple3.
346 // the first one is expanded to (0, 0, 1) while the second one
347 // is expaned to (0, 1, 1)
348 int ghostIndex = bendStamp->getGhostVectorSource();
349 std::vector<int>::iterator j =
350 std::find(bend.begin(), bend.end(), ghostIndex);
351 if (j != bend.end()) { bend.insert(j, ghostIndex); }
352 }
353
354 std::tuple<int, int, int> bendTuple {bend[0], bend[1], bend[2]};
355 auto& [first, second, third] = bendTuple;
356
357 // make sure the first element of bendTuple is always less than or equal
358 // to the third element of bendTuple
359 if (first > third) { std::swap(first, third); }
360
361 iter = allBends.find(bendTuple);
362 if (iter != allBends.end()) {
363 oss << "Error in Molecule " << getName() << ": "
364 << "Bend" << containerToString(bend) << " appears multiple times\n";
365 throw OpenMDException(oss.str());
366 } else {
367 allBends.insert(bendTuple);
368 }
369 }
370
371 for (std::size_t i = 0; i < getNBonds(); ++i) {
372 BondStamp* bondStamp = getBondStamp(i);
373 int a = bondStamp->getA();
374 int b = bondStamp->getB();
375
376 AtomStamp* atomA = getAtomStamp(a);
377 AtomStamp* atomB = getAtomStamp(b);
378
379 // find bend c--a--b
380 AtomStamp::AtomIter ai;
381 for (int c = atomA->getFirstBondedAtom(ai); c != -1;
382 c = atomA->getNextBondedAtom(ai)) {
383 if (b == c) continue;
384
385 std::tuple<int, int, int> newBend {c, a, b};
386 auto [first, second, third] = newBend;
387 if (first > third) { std::swap(first, third); }
388
389 if (allBends.find(newBend) == allBends.end()) {
390 allBends.insert(newBend);
391 BendStamp* newBendStamp = new BendStamp();
392 newBendStamp->setMembers(newBend);
393 addBendStamp(newBendStamp);
394 }
395 }
396
397 // find bend a--b--c
398 for (int c = atomB->getFirstBondedAtom(ai); c != -1;
399 c = atomB->getNextBondedAtom(ai)) {
400 if (a == c) continue;
401
402 std::tuple<int, int, int> newBend {a, b, c};
403 auto [first, second, third] = newBend;
404 if (first > third) { std::swap(first, third); }
405
406 if (allBends.find(newBend) == allBends.end()) {
407 allBends.insert(newBend);
408 BendStamp* newBendStamp = new BendStamp();
409 newBendStamp->setMembers(newBend);
410 addBendStamp(newBendStamp);
411 }
412 }
413 }
414 }
415
416 void MoleculeStamp::checkTorsions() {
417 std::ostringstream oss;
418 for (std::size_t i = 0; i < getNTorsions(); ++i) {
419 TorsionStamp* torsionStamp = getTorsionStamp(i);
420 std::vector<int> torsionAtoms = torsionStamp->getMembers();
421 std::vector<int>::iterator j =
422 std::find_if(torsionAtoms.begin(), torsionAtoms.end(),
423 std::bind(std::greater<int>(), std::placeholders::_1,
424 getNAtoms() - 1));
425 std::vector<int>::iterator k =
426 std::find_if(torsionAtoms.begin(), torsionAtoms.end(),
427 std::bind(std::less<int>(), std::placeholders::_1, 0));
428
429 if (j != torsionAtoms.end() || k != torsionAtoms.end()) {
430 oss << "Error in Molecule " << getName() << ": atoms of torsion"
431 << containerToString(torsionAtoms) << " have invalid indices\n";
432 throw OpenMDException(oss.str());
433 }
434 if (hasDuplicateElement(torsionAtoms)) {
435 oss << "Error in Molecule " << getName() << " : atoms of torsion"
436 << containerToString(torsionAtoms) << " have duplicated indices\n";
437 throw OpenMDException(oss.str());
438 }
439 }
440
441 for (std::size_t i = 0; i < getNTorsions(); ++i) {
442 TorsionStamp* torsionStamp = getTorsionStamp(i);
443 std::vector<int> torsionAtoms = torsionStamp->getMembers();
444 std::vector<int> rigidSet(getNRigidBodies(), 0);
445 std::vector<int>::iterator j;
446 for (j = torsionAtoms.begin(); j != torsionAtoms.end(); ++j) {
447 int rigidbodyIndex = atom2Rigidbody[*j];
448 if (rigidbodyIndex >= 0) {
449 ++rigidSet[rigidbodyIndex];
450 if (rigidSet[rigidbodyIndex] > 3) {
451 oss << "Error in Molecule " << getName() << ": torsion"
452 << containerToString(torsionAtoms)
453 << "has four atoms on the same rigid body\n";
454 throw OpenMDException(oss.str());
455 }
456 }
457 }
458 }
459
460 std::set<std::tuple<int, int, int, int>> allTorsions;
461 std::set<std::tuple<int, int, int, int>>::iterator iter;
462
463 for (std::size_t i = 0; i < getNTorsions(); ++i) {
464 TorsionStamp* torsionStamp = getTorsionStamp(i);
465 std::vector<int> torsion = torsionStamp->getMembers();
466 if (torsion.size() == 3) {
467 int ghostIndex = torsionStamp->getGhostVectorSource();
468 std::vector<int>::iterator j =
469 std::find(torsion.begin(), torsion.end(), ghostIndex);
470 if (j != torsion.end()) { torsion.insert(j, ghostIndex); }
471 }
472
473 std::tuple<int, int, int, int> torsionTuple {torsion[0], torsion[1],
474 torsion[2], torsion[3]};
475 auto& [first, second, third, fourth] = torsionTuple;
476
477 if (first > fourth) {
478 std::swap(first, fourth);
479 std::swap(second, third);
480 }
481
482 iter = allTorsions.find(torsionTuple);
483 if (iter == allTorsions.end()) {
484 allTorsions.insert(torsionTuple);
485 } else {
486 oss << "Error in Molecule " << getName() << ": "
487 << "Torsion" << containerToString(torsion)
488 << " appears multiple times\n";
489 throw OpenMDException(oss.str());
490 }
491 }
492
493 for (std::size_t i = 0; i < getNBonds(); ++i) {
494 BondStamp* bondStamp = getBondStamp(i);
495 int b = bondStamp->getA();
496 int c = bondStamp->getB();
497
498 AtomStamp* atomB = getAtomStamp(b);
499 AtomStamp* atomC = getAtomStamp(c);
500
501 AtomStamp::AtomIter ai2;
502 AtomStamp::AtomIter ai3;
503
504 for (int a = atomB->getFirstBondedAtom(ai2); a != -1;
505 a = atomB->getNextBondedAtom(ai2)) {
506 if (a == c) continue;
507
508 for (int d = atomC->getFirstBondedAtom(ai3); d != -1;
509 d = atomC->getNextBondedAtom(ai3)) {
510 if (d == b) continue;
511
512 std::tuple<int, int, int, int> newTorsion {a, b, c, d};
513 auto [first, second, third, fourth] = newTorsion;
514
515 // make sure the first element is always less than or equal
516 // to the fourth element in IntTuple4
517 if (first > fourth) {
518 std::swap(first, fourth);
519 std::swap(second, third);
520 }
521
522 if (allTorsions.find(newTorsion) == allTorsions.end()) {
523 allTorsions.insert(newTorsion);
524 TorsionStamp* newTorsionStamp = new TorsionStamp();
525 newTorsionStamp->setMembers(newTorsion);
526 addTorsionStamp(newTorsionStamp);
527 }
528 }
529 }
530 }
531 }
532
533 void MoleculeStamp::checkInversions() {
534 std::ostringstream oss;
535
536 // first we automatically find the other three atoms that
537 // are satellites of an inversion center:
538
539 for (std::size_t i = 0; i < getNInversions(); ++i) {
540 InversionStamp* inversionStamp = getInversionStamp(i);
541 int center = inversionStamp->getCenter();
542 std::vector<int> satellites;
543
544 // Some inversions come pre-programmed with the satellites. If
545 // so, don't add the satellites as they are already there.
546
547 if (inversionStamp->getNSatellites() != 3) {
548 for (std::size_t j = 0; j < getNBonds(); ++j) {
549 BondStamp* bondStamp = getBondStamp(j);
550 int a = bondStamp->getA();
551 int b = bondStamp->getB();
552
553 if (a == center) { satellites.push_back(b); }
554 if (b == center) { satellites.push_back(a); }
555 }
556
557 if (satellites.size() == 3) {
558 std::sort(satellites.begin(), satellites.end());
559 inversionStamp->setSatellites(satellites);
560 } else {
561 oss << "Error in Molecule " << getName() << ": found wrong number"
562 << " of bonds for inversion center " << center;
563 throw OpenMDException(oss.str());
564 }
565 }
566 }
567
568 // then we do some sanity checking on the inversions:
569
570 for (std::size_t i = 0; i < getNInversions(); ++i) {
571 InversionStamp* inversionStamp = getInversionStamp(i);
572
573 std::vector<int> inversionAtoms = inversionStamp->getSatellites();
574 // add the central atom to the beginning of the list:
575 inversionAtoms.insert(inversionAtoms.begin(),
576 inversionStamp->getCenter());
577
578 std::vector<int>::iterator j =
579 std::find_if(inversionAtoms.begin(), inversionAtoms.end(),
580 std::bind(std::greater<int>(), std::placeholders::_1,
581 getNAtoms() - 1));
582 std::vector<int>::iterator k =
583 std::find_if(inversionAtoms.begin(), inversionAtoms.end(),
584 std::bind(std::less<int>(), std::placeholders::_1, 0));
585
586 if (j != inversionAtoms.end() || k != inversionAtoms.end()) {
587 oss << "Error in Molecule " << getName() << ": atoms of inversion"
588 << containerToString(inversionAtoms) << " have invalid indices\n";
589 throw OpenMDException(oss.str());
590 }
591
592 if (hasDuplicateElement(inversionAtoms)) {
593 oss << "Error in Molecule " << getName() << " : atoms of inversion"
594 << containerToString(inversionAtoms)
595 << " have duplicated indices\n";
596 throw OpenMDException(oss.str());
597 }
598 }
599
600 for (std::size_t i = 0; i < getNInversions(); ++i) {
601 InversionStamp* inversionStamp = getInversionStamp(i);
602 std::vector<int> inversionAtoms = inversionStamp->getSatellites();
603 inversionAtoms.push_back(inversionStamp->getCenter());
604 std::vector<int> rigidSet(getNRigidBodies(), 0);
605 std::vector<int>::iterator j;
606 for (j = inversionAtoms.begin(); j != inversionAtoms.end(); ++j) {
607 int rigidbodyIndex = atom2Rigidbody[*j];
608 if (rigidbodyIndex >= 0) {
609 ++rigidSet[rigidbodyIndex];
610 if (rigidSet[rigidbodyIndex] > 3) {
611 oss << "Error in Molecule " << getName()
612 << ": inversion centered on atom "
613 << inversionStamp->getCenter()
614 << " has four atoms that belong to same rigidbody "
615 << rigidbodyIndex << "\n";
616 throw OpenMDException(oss.str());
617 }
618 }
619 }
620 }
621
622 std::set<std::tuple<int, int, int, int>> allInversions;
623 std::set<std::tuple<int, int, int, int>>::iterator iter;
624 for (std::size_t i = 0; i < getNInversions(); ++i) {
625 InversionStamp* inversionStamp = getInversionStamp(i);
626 int cent = inversionStamp->getCenter();
627 std::vector<int> inversion = inversionStamp->getSatellites();
628
629 std::tuple<int, int, int, int> inversionTuple {
630 cent, inversion[0], inversion[1], inversion[2]};
631 auto& [first, second, third, fourth] = inversionTuple;
632
633 // In OpenMD, the Central atom in an inversion comes first, and
634 // has a special position. The other three atoms can come in
635 // random order, and should be sorted in increasing numerical
636 // order to check for duplicates. This requires three pairwise
637 // swaps:
638 if (third > fourth) std::swap(third, fourth);
639 if (second > third) std::swap(second, third);
640 if (third > fourth) std::swap(third, fourth);
641
642 iter = allInversions.find(inversionTuple);
643 if (iter == allInversions.end()) {
644 allInversions.insert(inversionTuple);
645 } else {
646 oss << "Error in Molecule " << getName() << ": "
647 << "Inversion" << containerToString(inversion)
648 << " appears multiple times\n";
649 throw OpenMDException(oss.str());
650 }
651 }
652
653 // Next we automatically find the inversion centers that weren't
654 // explicitly created. An inversion center is any atom that has
655 // exactly three bonds to it. Not all inversion centers have
656 // potentials associated with them.
657
658 for (std::size_t i = 0; i < getNAtoms(); ++i) {
659 AtomStamp* ai = getAtomStamp(i);
660 if (ai->getCoordination() == 3) {
661 AtomStamp::AtomIter ai2;
662 std::vector<int> satellites;
663 for (int a = ai->getFirstBondedAtom(ai2); a != -1;
664 a = ai->getNextBondedAtom(ai2)) {
665 satellites.push_back(a);
666 }
667 if (satellites.size() == 3) {
668 int cent = ai->getIndex();
669 std::sort(satellites.begin(), satellites.end());
670
671 std::tuple<int, int, int, int> newInversion {
672 cent, satellites[0], satellites[1], satellites[2]};
673 auto [first, second, third, fourth] = newInversion;
674
675 if (third > fourth) std::swap(third, fourth);
676 if (second > third) std::swap(second, third);
677 if (third > fourth) std::swap(third, fourth);
678
679 if (allInversions.find(newInversion) == allInversions.end()) {
680 allInversions.insert(newInversion);
681 InversionStamp* newInversionStamp = new InversionStamp();
682 newInversionStamp->setCenter(cent);
683 newInversionStamp->setSatellites(satellites);
684 addInversionStamp(newInversionStamp);
685 }
686
687 } else {
688 oss << "Error in Molecule " << getName() << ": found bond mismatch"
689 << " when detecting inversion centers.";
690 throw OpenMDException(oss.str());
691 }
692 }
693 }
694 }
695
696 void MoleculeStamp::checkRigidBodies() {
697 std::ostringstream oss;
698 std::vector<RigidBodyStamp*>::iterator ri =
699 std::find(rigidBodyStamps_.begin(), rigidBodyStamps_.end(),
700 static_cast<RigidBodyStamp*>(NULL));
701 if (ri != rigidBodyStamps_.end()) {
702 oss << "Error in Molecule " << getName() << ":rigidBody["
703 << ri - rigidBodyStamps_.begin() << "] is missing\n";
704 throw OpenMDException(oss.str());
705 }
706
707 for (std::size_t i = 0; i < getNRigidBodies(); ++i) {
708 RigidBodyStamp* rbStamp = getRigidBodyStamp(i);
709 std::vector<int> rigidAtoms = rbStamp->getMembers();
710 std::vector<int>::iterator j =
711 std::find_if(rigidAtoms.begin(), rigidAtoms.end(),
712 std::bind(std::greater<int>(), std::placeholders::_1,
713 getNAtoms() - 1));
714 if (j != rigidAtoms.end()) {
715 oss << "Error in Molecule " << getName();
716 throw OpenMDException(oss.str());
717 }
718 }
719 }
720
721 void MoleculeStamp::checkCutoffGroups() {
722 std::vector<AtomStamp*>::iterator ai;
723 std::vector<int>::iterator fai;
724
725 // add all atoms into freeAtoms_ set
726 for (ai = atomStamps_.begin(); ai != atomStamps_.end(); ++ai) {
727 freeAtoms_.push_back((*ai)->getIndex());
728 }
729
730 for (std::size_t i = 0; i < getNCutoffGroups(); ++i) {
731 CutoffGroupStamp* cutoffGroupStamp = getCutoffGroupStamp(i);
732 std::vector<int> cutoffGroupAtoms = cutoffGroupStamp->getMembers();
733 std::vector<int>::iterator j =
734 std::find_if(cutoffGroupAtoms.begin(), cutoffGroupAtoms.end(),
735 std::bind(std::greater<int>(), std::placeholders::_1,
736 getNAtoms() - 1));
737 if (j != cutoffGroupAtoms.end()) {
738 std::ostringstream oss;
739 oss << "Error in Molecule " << getName() << ": cutoffGroup"
740 << " is out of range\n";
741 throw OpenMDException(oss.str());
742 }
743
744 for (fai = cutoffGroupAtoms.begin(); fai != cutoffGroupAtoms.end();
745 ++fai) {
746 // erase the atoms belonging to cutoff groups from freeAtoms_ vector
747 freeAtoms_.erase(
748 std::remove(freeAtoms_.begin(), freeAtoms_.end(), (*fai)),
749 freeAtoms_.end());
750 }
751 }
752 }
753
754 void MoleculeStamp::checkFragments() {
755 std::vector<FragmentStamp*>::iterator fi =
756 std::find(fragmentStamps_.begin(), fragmentStamps_.end(),
757 static_cast<FragmentStamp*>(NULL));
758 if (fi != fragmentStamps_.end()) {
759 std::ostringstream oss;
760 oss << "Error in Molecule " << getName() << ":fragment["
761 << fi - fragmentStamps_.begin() << "] is missing\n";
762 throw OpenMDException(oss.str());
763 }
764 }
765
766 void MoleculeStamp::checkConstraints() {
767 std::ostringstream oss;
768 // make sure index is not out of range
769 int natoms = getNAtoms();
770 for (std::size_t i = 0; i < getNConstraints(); ++i) {
771 ConstraintStamp* constraintStamp = getConstraintStamp(i);
772 if (constraintStamp->getA() > natoms - 1 || constraintStamp->getA() < 0 ||
773 constraintStamp->getB() > natoms - 1 || constraintStamp->getB() < 0 ||
774 constraintStamp->getA() == constraintStamp->getB()) {
775 oss << "Error in Molecule " << getName() << ": constraint("
776 << constraintStamp->getA() << ", " << constraintStamp->getB()
777 << ") is invalid\n";
778 throw OpenMDException(oss.str());
779 }
780 }
781
782 // make sure constraints are unique
783 std::set<std::pair<int, int>> allConstraints;
784 for (std::size_t i = 0; i < getNConstraints(); ++i) {
785 ConstraintStamp* constraintStamp = getConstraintStamp(i);
786 std::pair<int, int> constraintPair(constraintStamp->getA(),
787 constraintStamp->getB());
788 // make sure constraintPair.first is always less than or equal to
789 // constraintPair.third
790 if (constraintPair.first > constraintPair.second) {
791 std::swap(constraintPair.first, constraintPair.second);
792 }
793
794 std::set<std::pair<int, int>>::iterator iter =
795 allConstraints.find(constraintPair);
796 if (iter != allConstraints.end()) {
797 oss << "Error in Molecule " << getName() << ": "
798 << "constraint(" << iter->first << ", " << iter->second
799 << ") appears multiple times\n";
800 throw OpenMDException(oss.str());
801 } else {
802 allConstraints.insert(constraintPair);
803 }
804 }
805
806 // make sure atoms belong to same rigidbody are not constrained to
807 // each other
808 for (std::size_t i = 0; i < getNConstraints(); ++i) {
809 ConstraintStamp* constraintStamp = getConstraintStamp(i);
810 if (atom2Rigidbody[constraintStamp->getA()] ==
811 atom2Rigidbody[constraintStamp->getB()]) {
812 oss << "Error in Molecule " << getName() << ": "
813 << "constraint(" << constraintStamp->getA() << ", "
814 << constraintStamp->getB() << ") belong to same rigidbody "
815 << atom2Rigidbody[constraintStamp->getA()] << "\n";
816 throw OpenMDException(oss.str());
817 }
818 }
819 }
820
821 void MoleculeStamp::fillBondInfo() {
822 for (std::size_t i = 0; i < getNBonds(); ++i) {
823 BondStamp* bondStamp = getBondStamp(i);
824 int a = bondStamp->getA();
825 int b = bondStamp->getB();
826 AtomStamp* atomA = getAtomStamp(a);
827 AtomStamp* atomB = getAtomStamp(b);
828 atomA->addBond(i);
829 atomA->addBondedAtom(b);
830 atomB->addBond(i);
831 atomB->addBondedAtom(a);
832 }
833 }
834
835 // Function Name: isBondInSameRigidBody
836 // Returns true is both atoms of the bond belong to the same rigid
837 // body, otherwise return false
838 bool MoleculeStamp::isBondInSameRigidBody(BondStamp* bond) {
839 int rbA;
840 int rbB;
841 int consAtomA;
842 int consAtomB;
843
844 if (!isAtomInRigidBody(bond->getA(), rbA, consAtomA)) return false;
845
846 if (!isAtomInRigidBody(bond->getB(), rbB, consAtomB)) return false;
847
848 if (rbB == rbA)
849 return true;
850 else
851 return false;
852 }
853
854 // Function Name: isAtomInRigidBody
855 // Returns false if atom does not belong to a rigid body, otherwise
856 // returns true
857 bool MoleculeStamp::isAtomInRigidBody(int atomIndex) {
858 return atom2Rigidbody[atomIndex] >= 0;
859 }
860
861 // Function Name: isAtomInRigidBody
862 // Returns false if atom does not belong to a rigid body otherwise
863 // returns true and sets whichRigidBody and consAtomIndex
864 // atomIndex : the index of atom in component
865 // whichRigidBody: the index of the rigidbody in the component
866 // consAtomIndex: the position the joint atom appears in the rigidbody's
867 // definition
868 bool MoleculeStamp::isAtomInRigidBody(int atomIndex, int& whichRigidBody,
869 int& consAtomIndex) {
870 whichRigidBody = -1;
871 consAtomIndex = -1;
872
873 if (atom2Rigidbody[atomIndex] >= 0) {
874 whichRigidBody = atom2Rigidbody[atomIndex];
875 RigidBodyStamp* rbStamp = getRigidBodyStamp(whichRigidBody);
876 int numAtom = rbStamp->getNMembers();
877 for (int j = 0; j < numAtom; j++) {
878 if (rbStamp->getMemberAt(j) == atomIndex) {
879 consAtomIndex = j;
880 return true;
881 }
882 }
883 }
884
885 return false;
886 }
887
888 // Returns the position of joint atoms apearing in a rigidbody's definition
889 // For the time being, we will use the most inefficient algorithm,
890 // the complexity is O(N^2). We could improve the
891 // complexity to O(NlogN) by sorting the atom index in rigid body
892 // first
893 std::vector<std::pair<int, int>> MoleculeStamp::getJointAtoms(int rb1,
894 int rb2) {
895 RigidBodyStamp* rbStamp1;
896 RigidBodyStamp* rbStamp2;
897 int natomInRb1;
898 int natomInRb2;
899 int atomIndex1;
900 int atomIndex2;
901 std::vector<std::pair<int, int>> jointAtomIndexPair;
902
903 rbStamp1 = this->getRigidBodyStamp(rb1);
904 natomInRb1 = rbStamp1->getNMembers();
905
906 rbStamp2 = this->getRigidBodyStamp(rb2);
907 natomInRb2 = rbStamp2->getNMembers();
908
909 for (int i = 0; i < natomInRb1; i++) {
910 atomIndex1 = rbStamp1->getMemberAt(i);
911
912 for (int j = 0; j < natomInRb2; j++) {
913 atomIndex2 = rbStamp2->getMemberAt(j);
914
915 if (atomIndex1 == atomIndex2) {
916 jointAtomIndexPair.push_back(std::make_pair(i, j));
917 break;
918 }
919 }
920 }
921
922 return jointAtomIndexPair;
923 }
924} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.