OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
FragmentStamp.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 following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47#include "types/FragmentStamp.hpp"
48
49#include <algorithm>
50#include <functional>
51#include <iostream>
52#include <sstream>
53#include <tuple>
54
55#include "utils/MemoryUtils.hpp"
56
57using namespace std;
58
59namespace OpenMD {
60
61 template<class ContainerType>
62 bool hasDuplicateElement(const ContainerType& cont) {
63 ContainerType tmp = cont;
64 std::sort(tmp.begin(), tmp.end());
65 tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
66 return tmp.size() != cont.size();
67 }
68
69 FragmentStamp::FragmentStamp() { DefineParameter(Name, "name"); }
70
71 FragmentStamp::~FragmentStamp() {
72 Utils::deletePointers(atomStamps_);
73 Utils::deletePointers(bondStamps_);
74 Utils::deletePointers(bendStamps_);
75 Utils::deletePointers(torsionStamps_);
76 Utils::deletePointers(inversionStamps_);
77 Utils::deletePointers(rigidBodyStamps_);
78 Utils::deletePointers(cutoffGroupStamps_);
79 Utils::deletePointers(nodesStamps_);
80 Utils::deletePointers(constraintStamps_);
81 }
82
83 bool FragmentStamp::addAtomStamp(AtomStamp* atom) {
84 bool ret = addIndexSensitiveStamp(atomStamps_, atom);
85 if (!ret) {
86 std::ostringstream oss;
87 oss << "Error in Fragment " << getName()
88 << ": multiple atoms have the same indices" << atom->getIndex()
89 << "\n";
90 throw OpenMDException(oss.str());
91 }
92 return ret;
93 }
94
95 bool FragmentStamp::addBondStamp(BondStamp* bond) {
96 bondStamps_.push_back(bond);
97 return true;
98 }
99
100 bool FragmentStamp::addBendStamp(BendStamp* bend) {
101 bendStamps_.push_back(bend);
102 return true;
103 }
104
105 bool FragmentStamp::addTorsionStamp(TorsionStamp* torsion) {
106 torsionStamps_.push_back(torsion);
107 return true;
108 }
109 bool FragmentStamp::addInversionStamp(InversionStamp* inversion) {
110 inversionStamps_.push_back(inversion);
111 return true;
112 }
113
114 bool FragmentStamp::addRigidBodyStamp(RigidBodyStamp* rigidbody) {
115 bool ret = addIndexSensitiveStamp(rigidBodyStamps_, rigidbody);
116 if (!ret) {
117 std::ostringstream oss;
118 oss << "Error in Fragment " << getName()
119 << ": multiple rigidbodies have the same indices: "
120 << rigidbody->getIndex() << "\n";
121 throw OpenMDException(oss.str());
122 }
123 return ret;
124 }
125
126 bool FragmentStamp::addCutoffGroupStamp(CutoffGroupStamp* cutoffgroup) {
127 cutoffGroupStamps_.push_back(cutoffgroup);
128 return true;
129 }
130
131 bool FragmentStamp::addNodesStamp(NodesStamp* nodes) {
132 nodesStamps_.push_back(nodes);
133 return true;
134 }
135
136 bool FragmentStamp::addConstraintStamp(ConstraintStamp* constraint) {
137 constraintStamps_.push_back(constraint);
138 return true;
139 }
140
141 void FragmentStamp::validate() {
142 DataHolder::validate();
143 CheckParameter(Name, isNotEmpty());
144
145 atom2Rigidbody.resize(getNAtoms());
146
147 // A negative number means the atom is a free atom, and does not
148 // belong to rigidbody. Every element in atom2Rigidbody has unique
149 // negative number at the very beginning
150
151 for (unsigned int i = 0; i < atom2Rigidbody.size(); ++i) {
152 atom2Rigidbody[i] = -1 - int(i);
153 }
154 for (std::size_t i = 0; i < getNRigidBodies(); ++i) {
155 RigidBodyStamp* rbStamp = getRigidBodyStamp(i);
156 std::vector<int> members = rbStamp->getMembers();
157 for (std::vector<int>::iterator j = members.begin(); j != members.end();
158 ++j) {
159 atom2Rigidbody[*j] = i;
160 }
161 }
162 checkAtoms();
163 checkBonds();
164 fillBondInfo();
165 checkBends();
166 checkTorsions();
167 checkInversions();
168 checkRigidBodies();
169 checkCutoffGroups();
170 checkConstraints();
171 checkNodes();
172 }
173
174 void FragmentStamp::checkAtoms() {
175 std::vector<AtomStamp*>::iterator ai = std::find(
176 atomStamps_.begin(), atomStamps_.end(), static_cast<AtomStamp*>(NULL));
177 if (ai != atomStamps_.end()) {
178 std::ostringstream oss;
179 oss << "Error in Fragment " << getName() << ": atom["
180 << ai - atomStamps_.begin() << "] is missing\n";
181 throw OpenMDException(oss.str());
182 }
183 }
184
185 void FragmentStamp::checkBonds() {
186 std::ostringstream oss;
187 // make sure index is not out of range
188 int natoms = getNAtoms();
189 for (std::size_t i = 0; i < getNBonds(); ++i) {
190 BondStamp* bondStamp = getBondStamp(i);
191 if (bondStamp->getA() > natoms - 1 || bondStamp->getA() < 0 ||
192 bondStamp->getB() > natoms - 1 || bondStamp->getB() < 0 ||
193 bondStamp->getA() == bondStamp->getB()) {
194 oss << "Error in Fragment " << getName() << ": bond("
195 << bondStamp->getA() << ", " << bondStamp->getB()
196 << ") is invalid\n";
197 throw OpenMDException(oss.str());
198 }
199 }
200
201 // make sure bonds are unique
202 std::set<std::pair<int, int>> allBonds;
203 for (std::size_t i = 0; i < getNBonds(); ++i) {
204 BondStamp* bondStamp = getBondStamp(i);
205 std::pair<int, int> bondPair(bondStamp->getA(), bondStamp->getB());
206 // make sure bondPair.first is always less than or equal to
207 // bondPair.third
208 if (bondPair.first > bondPair.second) {
209 std::swap(bondPair.first, bondPair.second);
210 }
211
212 std::set<std::pair<int, int>>::iterator iter = allBonds.find(bondPair);
213 if (iter != allBonds.end()) {
214 oss << "Error in Fragment " << getName() << ": "
215 << "bond(" << iter->first << ", " << iter->second
216 << ") appears multiple times\n";
217 throw OpenMDException(oss.str());
218 } else {
219 allBonds.insert(bondPair);
220 }
221 }
222
223 // make sure atoms belong to same rigidbody do not bond to each other
224 for (std::size_t i = 0; i < getNBonds(); ++i) {
225 BondStamp* bondStamp = getBondStamp(i);
226 if (atom2Rigidbody[bondStamp->getA()] ==
227 atom2Rigidbody[bondStamp->getB()]) {
228 oss << "Error in Fragment " << getName() << ": "
229 << "bond(" << bondStamp->getA() << ", " << bondStamp->getB()
230 << ") belong to same rigidbody "
231 << atom2Rigidbody[bondStamp->getA()] << "\n";
232 throw OpenMDException(oss.str());
233 }
234 }
235 }
236
237 void FragmentStamp::checkBends() {
238 std::ostringstream oss;
239 for (std::size_t i = 0; i < getNBends(); ++i) {
240 BendStamp* bendStamp = getBendStamp(i);
241 std::vector<int> bendAtoms = bendStamp->getMembers();
242 std::vector<int>::iterator j = std::find_if(
243 bendAtoms.begin(), bendAtoms.end(),
244 std::bind(std::greater<int>(), placeholders::_1, getNAtoms() - 1));
245 std::vector<int>::iterator k =
246 std::find_if(bendAtoms.begin(), bendAtoms.end(),
247 std::bind(std::less<int>(), placeholders::_1, 0));
248
249 if (j != bendAtoms.end() || k != bendAtoms.end()) {
250 oss << "Error in Fragment " << getName() << " : atoms of bend"
251 << containerToString(bendAtoms) << " have invalid indices\n";
252 throw OpenMDException(oss.str());
253 }
254
255 if (hasDuplicateElement(bendAtoms)) {
256 oss << "Error in Fragment " << getName() << " : atoms of bend"
257 << containerToString(bendAtoms) << " have duplicated indices\n";
258 throw OpenMDException(oss.str());
259 }
260
261 if (bendAtoms.size() == 2) {
262 if (!bendStamp->haveGhostVectorSource()) {
263 oss << "Error in Fragment " << getName()
264 << ": ghostVectorSouce is missing\n";
265 throw OpenMDException(oss.str());
266 } else {
267 std::size_t ghostIndex = bendStamp->getGhostVectorSource();
268 if (ghostIndex < getNAtoms()) {
269 if (std::find(bendAtoms.begin(), bendAtoms.end(), ghostIndex) ==
270 bendAtoms.end()) {
271 oss << "Error in Fragment " << getName() << ": ghostVectorSouce "
272 << ghostIndex << "is invalid\n";
273 throw OpenMDException(oss.str());
274 }
275 if (!getAtomStamp(ghostIndex)->haveOrientation()) {
276 oss << "Error in Fragment " << getName()
277 << ": ghost atom must be a directional atom\n";
278 throw OpenMDException(oss.str());
279 }
280 } else {
281 oss << "Error in Fragment " << getName() << ": ghostVectorSource "
282 << ghostIndex << " is invalid\n";
283 throw OpenMDException(oss.str());
284 }
285 }
286 } else if (bendAtoms.size() == 3 && bendStamp->haveGhostVectorSource()) {
287 oss << "Error in Fragment " << getName()
288 << ": normal bend should not have ghostVectorSouce\n";
289 throw OpenMDException(oss.str());
290 }
291 }
292
293 for (std::size_t i = 0; i < getNBends(); ++i) {
294 BendStamp* bendStamp = getBendStamp(i);
295 std::vector<int> bendAtoms = bendStamp->getMembers();
296 std::vector<int> rigidSet(getNRigidBodies(), 0);
297 std::vector<int>::iterator j;
298 for (j = bendAtoms.begin(); j != bendAtoms.end(); ++j) {
299 int rigidbodyIndex = atom2Rigidbody[*j];
300 if (rigidbodyIndex >= 0) {
301 ++rigidSet[rigidbodyIndex];
302 if (rigidSet[rigidbodyIndex] > 2) {
303 oss << "Error in Fragment " << getName() << ": bend"
304 << containerToString(bendAtoms)
305 << "has three atoms on the same rigid body\n";
306 throw OpenMDException(oss.str());
307 }
308 }
309 }
310 }
311
312 std::set<std::tuple<int, int, int>> allBends;
313 std::set<std::tuple<int, int, int>>::iterator iter;
314 for (std::size_t i = 0; i < getNBends(); ++i) {
315 BendStamp* bendStamp = getBendStamp(i);
316 std::vector<int> bend = bendStamp->getMembers();
317 if (bend.size() == 2) {
318 // in case we have two ghost bend. For example,
319 // bend {
320 // members (0, 1);
321 // ghostVectorSource = 0;
322 // }
323 // and
324 // bend {
325 // members (0, 1);
326 // ghostVectorSource = 0;
327 // }
328 // In order to distinguish them. we expand them to Tuple3.
329 // the first one is expanded to (0, 0, 1) while the second one
330 // is expaned to (0, 1, 1)
331 int ghostIndex = bendStamp->getGhostVectorSource();
332 std::vector<int>::iterator j =
333 std::find(bend.begin(), bend.end(), ghostIndex);
334 if (j != bend.end()) { bend.insert(j, ghostIndex); }
335 }
336
337 std::tuple<int, int, int> bendTuple {bend[0], bend[1], bend[2]};
338 auto& [first, second, third] = bendTuple;
339
340 // make sure bendTuple.first is always less than or equal to
341 // bendTuple.third
342 if (first > third) { std::swap(first, third); }
343
344 iter = allBends.find(bendTuple);
345 if (iter != allBends.end()) {
346 oss << "Error in Fragment " << getName() << ": "
347 << "Bend" << containerToString(bend) << " appears multiple times\n";
348 throw OpenMDException(oss.str());
349 } else {
350 allBends.insert(bendTuple);
351 }
352 }
353 }
354
355 void FragmentStamp::checkTorsions() {
356 std::ostringstream oss;
357 for (std::size_t i = 0; i < getNTorsions(); ++i) {
358 TorsionStamp* torsionStamp = getTorsionStamp(i);
359 std::vector<int> torsionAtoms = torsionStamp->getMembers();
360 std::vector<int>::iterator j = std::find_if(
361 torsionAtoms.begin(), torsionAtoms.end(),
362 std::bind(std::greater<int>(), placeholders::_1, getNAtoms() - 1));
363 std::vector<int>::iterator k =
364 std::find_if(torsionAtoms.begin(), torsionAtoms.end(),
365 std::bind(std::less<int>(), placeholders::_1, 0));
366
367 if (j != torsionAtoms.end() || k != torsionAtoms.end()) {
368 oss << "Error in Fragment " << getName() << ": atoms of torsion"
369 << containerToString(torsionAtoms) << " have invalid indices\n";
370 throw OpenMDException(oss.str());
371 }
372 if (hasDuplicateElement(torsionAtoms)) {
373 oss << "Error in Fragment " << getName() << " : atoms of torsion"
374 << containerToString(torsionAtoms) << " have duplicated indices\n";
375 throw OpenMDException(oss.str());
376 }
377 }
378
379 for (std::size_t i = 0; i < getNTorsions(); ++i) {
380 TorsionStamp* torsionStamp = getTorsionStamp(i);
381 std::vector<int> torsionAtoms = torsionStamp->getMembers();
382 std::vector<int> rigidSet(getNRigidBodies(), 0);
383 std::vector<int>::iterator j;
384 for (j = torsionAtoms.begin(); j != torsionAtoms.end(); ++j) {
385 int rigidbodyIndex = atom2Rigidbody[*j];
386 if (rigidbodyIndex >= 0) {
387 ++rigidSet[rigidbodyIndex];
388 if (rigidSet[rigidbodyIndex] > 3) {
389 oss << "Error in Fragment " << getName() << ": torsion"
390 << containerToString(torsionAtoms)
391 << "has four atoms on the same rigid body\n";
392 throw OpenMDException(oss.str());
393 }
394 }
395 }
396 }
397
398 std::set<std::tuple<int, int, int, int>> allTorsions;
399 std::set<std::tuple<int, int, int, int>>::iterator iter;
400 for (std::size_t i = 0; i < getNTorsions(); ++i) {
401 TorsionStamp* torsionStamp = getTorsionStamp(i);
402 std::vector<int> torsion = torsionStamp->getMembers();
403 if (torsion.size() == 3) {
404 int ghostIndex = torsionStamp->getGhostVectorSource();
405 std::vector<int>::iterator j =
406 std::find(torsion.begin(), torsion.end(), ghostIndex);
407 if (j != torsion.end()) { torsion.insert(j, ghostIndex); }
408 }
409
410 std::tuple<int, int, int, int> torsionTuple(torsion[0], torsion[1],
411 torsion[2], torsion[3]);
412 auto& [first, second, third, fourth] = torsionTuple;
413
414 if (first > fourth) {
415 std::swap(first, fourth);
416 std::swap(second, third);
417 }
418
419 iter = allTorsions.find(torsionTuple);
420 if (iter == allTorsions.end()) {
421 allTorsions.insert(torsionTuple);
422 } else {
423 oss << "Error in Fragment " << getName() << ": "
424 << "Torsion" << containerToString(torsion)
425 << " appears multiple times\n";
426 throw OpenMDException(oss.str());
427 }
428 }
429 }
430
431 void FragmentStamp::checkInversions() {
432 std::ostringstream oss;
433
434 // first we automatically find the other three atoms that
435 // are satellites of an inversion center:
436
437 for (std::size_t i = 0; i < getNInversions(); ++i) {
438 InversionStamp* inversionStamp = getInversionStamp(i);
439 int center = inversionStamp->getCenter();
440 std::vector<int> satellites;
441
442 // Some inversions come pre-programmed with the satellites. If
443 // so, don't add the satellites as they are already there.
444
445 if (inversionStamp->getNSatellites() != 3) {
446 for (std::size_t j = 0; j < getNBonds(); ++j) {
447 BondStamp* bondStamp = getBondStamp(j);
448 int a = bondStamp->getA();
449 int b = bondStamp->getB();
450
451 if (a == center) { satellites.push_back(b); }
452 if (b == center) { satellites.push_back(a); }
453 }
454
455 if (satellites.size() == 3) {
456 std::sort(satellites.begin(), satellites.end());
457 inversionStamp->setSatellites(satellites);
458 } else {
459 oss << "Error in Fragment " << getName() << ": found wrong number"
460 << " of bonds for inversion center " << center;
461 throw OpenMDException(oss.str());
462 }
463 }
464 }
465
466 // then we do some sanity checking on the inversions:
467
468 for (std::size_t i = 0; i < getNInversions(); ++i) {
469 InversionStamp* inversionStamp = getInversionStamp(i);
470
471 std::vector<int> inversionAtoms = inversionStamp->getSatellites();
472 // add the central atom to the beginning of the list:
473 inversionAtoms.insert(inversionAtoms.begin(),
474 inversionStamp->getCenter());
475
476 std::vector<int>::iterator j = std::find_if(
477 inversionAtoms.begin(), inversionAtoms.end(),
478 std::bind(std::greater<int>(), placeholders::_1, getNAtoms() - 1));
479 std::vector<int>::iterator k =
480 std::find_if(inversionAtoms.begin(), inversionAtoms.end(),
481 std::bind(std::less<int>(), placeholders::_1, 0));
482
483 if (j != inversionAtoms.end() || k != inversionAtoms.end()) {
484 oss << "Error in Fragment " << getName() << ": atoms of inversion"
485 << containerToString(inversionAtoms) << " have invalid indices\n";
486 throw OpenMDException(oss.str());
487 }
488
489 if (hasDuplicateElement(inversionAtoms)) {
490 oss << "Error in Fragment " << getName() << " : atoms of inversion"
491 << containerToString(inversionAtoms)
492 << " have duplicated indices\n";
493 throw OpenMDException(oss.str());
494 }
495 }
496
497 for (std::size_t i = 0; i < getNInversions(); ++i) {
498 InversionStamp* inversionStamp = getInversionStamp(i);
499 std::vector<int> inversionAtoms = inversionStamp->getSatellites();
500 inversionAtoms.push_back(inversionStamp->getCenter());
501 std::vector<int> rigidSet(getNRigidBodies(), 0);
502 std::vector<int>::iterator j;
503 for (j = inversionAtoms.begin(); j != inversionAtoms.end(); ++j) {
504 int rigidbodyIndex = atom2Rigidbody[*j];
505 if (rigidbodyIndex >= 0) {
506 ++rigidSet[rigidbodyIndex];
507 if (rigidSet[rigidbodyIndex] > 3) {
508 oss << "Error in Fragment " << getName()
509 << ": inversion centered on atom "
510 << inversionStamp->getCenter()
511 << " has four atoms that belong to same rigidbody "
512 << rigidbodyIndex << "\n";
513 throw OpenMDException(oss.str());
514 }
515 }
516 }
517 }
518
519 std::set<std::tuple<int, int, int, int>> allInversions;
520 std::set<std::tuple<int, int, int, int>>::iterator iter;
521 for (std::size_t i = 0; i < getNInversions(); ++i) {
522 InversionStamp* inversionStamp = getInversionStamp(i);
523 int cent = inversionStamp->getCenter();
524 std::vector<int> inversion = inversionStamp->getSatellites();
525
526 std::tuple<int, int, int, int> inversionTuple(cent, inversion[0],
527 inversion[1], inversion[2]);
528 auto& [first, second, third, fourth] = inversionTuple;
529
530 // In OpenMD, the Central atom in an inversion comes first, and
531 // has a special position. The other three atoms can come in
532 // random order, and should be sorted in increasing numerical
533 // order to check for duplicates. This requires three pairwise
534 // swaps:
535 if (third > fourth) std::swap(third, fourth);
536 if (second > third) std::swap(second, third);
537 if (third > fourth) std::swap(third, fourth);
538
539 iter = allInversions.find(inversionTuple);
540 if (iter == allInversions.end()) {
541 allInversions.insert(inversionTuple);
542 } else {
543 oss << "Error in Fragment " << getName() << ": "
544 << "Inversion" << containerToString(inversion)
545 << " appears multiple times\n";
546 throw OpenMDException(oss.str());
547 }
548 }
549 }
550
551 void FragmentStamp::checkRigidBodies() {
552 std::ostringstream oss;
553 std::vector<RigidBodyStamp*>::iterator ri =
554 std::find(rigidBodyStamps_.begin(), rigidBodyStamps_.end(),
555 static_cast<RigidBodyStamp*>(NULL));
556 if (ri != rigidBodyStamps_.end()) {
557 oss << "Error in Fragment " << getName() << ":rigidBody["
558 << ri - rigidBodyStamps_.begin() << "] is missing\n";
559 throw OpenMDException(oss.str());
560 }
561
562 for (std::size_t i = 0; i < getNRigidBodies(); ++i) {
563 RigidBodyStamp* rbStamp = getRigidBodyStamp(i);
564 std::vector<int> rigidAtoms = rbStamp->getMembers();
565 std::vector<int>::iterator j = std::find_if(
566 rigidAtoms.begin(), rigidAtoms.end(),
567 std::bind(std::greater<int>(), placeholders::_1, getNAtoms() - 1));
568 if (j != rigidAtoms.end()) {
569 oss << "Error in Fragment " << getName();
570 throw OpenMDException(oss.str());
571 }
572 }
573 }
574
575 void FragmentStamp::checkCutoffGroups() {
576 std::vector<AtomStamp*>::iterator ai;
577 std::vector<int>::iterator fai;
578
579 // add all atoms into freeAtoms_ set
580 for (ai = atomStamps_.begin(); ai != atomStamps_.end(); ++ai) {
581 freeAtoms_.push_back((*ai)->getIndex());
582 }
583
584 for (std::size_t i = 0; i < getNCutoffGroups(); ++i) {
585 CutoffGroupStamp* cutoffGroupStamp = getCutoffGroupStamp(i);
586 std::vector<int> cutoffGroupAtoms = cutoffGroupStamp->getMembers();
587 std::vector<int>::iterator j = std::find_if(
588 cutoffGroupAtoms.begin(), cutoffGroupAtoms.end(),
589 std::bind(std::greater<int>(), placeholders::_1, getNAtoms() - 1));
590 if (j != cutoffGroupAtoms.end()) {
591 std::ostringstream oss;
592 oss << "Error in Fragment " << getName() << ": cutoffGroup"
593 << " is out of range\n";
594 throw OpenMDException(oss.str());
595 }
596
597 for (fai = cutoffGroupAtoms.begin(); fai != cutoffGroupAtoms.end();
598 ++fai) {
599 // erase the atoms belonging to cutoff groups from freeAtoms_ vector
600 freeAtoms_.erase(
601 std::remove(freeAtoms_.begin(), freeAtoms_.end(), (*fai)),
602 freeAtoms_.end());
603 }
604 }
605 }
606
607 void FragmentStamp::checkConstraints() {
608 std::ostringstream oss;
609 // make sure index is not out of range
610 int natoms = getNAtoms();
611 for (std::size_t i = 0; i < getNConstraints(); ++i) {
612 ConstraintStamp* constraintStamp = getConstraintStamp(i);
613 if (constraintStamp->getA() > natoms - 1 || constraintStamp->getA() < 0 ||
614 constraintStamp->getB() > natoms - 1 || constraintStamp->getB() < 0 ||
615 constraintStamp->getA() == constraintStamp->getB()) {
616 oss << "Error in Fragment " << getName() << ": constraint("
617 << constraintStamp->getA() << ", " << constraintStamp->getB()
618 << ") is invalid\n";
619 throw OpenMDException(oss.str());
620 }
621 }
622
623 // make sure constraints are unique
624 std::set<std::pair<int, int>> allConstraints;
625 for (std::size_t i = 0; i < getNConstraints(); ++i) {
626 ConstraintStamp* constraintStamp = getConstraintStamp(i);
627 std::pair<int, int> constraintPair(constraintStamp->getA(),
628 constraintStamp->getB());
629 // make sure constraintPair.first is always less than or equal to
630 // constraintPair.third
631 if (constraintPair.first > constraintPair.second) {
632 std::swap(constraintPair.first, constraintPair.second);
633 }
634
635 std::set<std::pair<int, int>>::iterator iter =
636 allConstraints.find(constraintPair);
637 if (iter != allConstraints.end()) {
638 oss << "Error in Fragment " << getName() << ": "
639 << "constraint(" << iter->first << ", " << iter->second
640 << ") appears multiple times\n";
641 throw OpenMDException(oss.str());
642 } else {
643 allConstraints.insert(constraintPair);
644 }
645 }
646
647 // make sure atoms belong to same rigidbody are not constrained to
648 // each other
649 for (std::size_t i = 0; i < getNConstraints(); ++i) {
650 ConstraintStamp* constraintStamp = getConstraintStamp(i);
651 if (atom2Rigidbody[constraintStamp->getA()] ==
652 atom2Rigidbody[constraintStamp->getB()]) {
653 oss << "Error in Fragment " << getName() << ": "
654 << "constraint(" << constraintStamp->getA() << ", "
655 << constraintStamp->getB() << ") belong to same rigidbody "
656 << atom2Rigidbody[constraintStamp->getA()] << "\n";
657 throw OpenMDException(oss.str());
658 }
659 }
660 }
661
662 void FragmentStamp::checkNodes() {
663 std::ostringstream oss;
664 std::vector<NodesStamp*>::iterator ni =
665 std::find(nodesStamps_.begin(), nodesStamps_.end(),
666 static_cast<NodesStamp*>(NULL));
667 if (ni != nodesStamps_.end()) {
668 oss << "Error in Molecule " << getName() << ":nodes["
669 << ni - nodesStamps_.begin() << "] is missing\n";
670 throw OpenMDException(oss.str());
671 }
672
673 for (std::size_t i = 0; i < getNNodes(); ++i) {
674 NodesStamp* nStamp = getNodesStamp(i);
675 std::vector<int> nodeAtoms = nStamp->getMembers();
676 std::vector<int>::iterator j = std::find_if(
677 nodeAtoms.begin(), nodeAtoms.end(),
678 std::bind(std::greater<int>(), placeholders::_1, getNAtoms() - 1));
679 if (j != nodeAtoms.end()) {
680 oss << "Error in Fragment " << getName();
681 throw OpenMDException(oss.str());
682 }
683 }
684 }
685
686 void FragmentStamp::fillBondInfo() {
687 for (std::size_t i = 0; i < getNBonds(); ++i) {
688 BondStamp* bondStamp = getBondStamp(i);
689 int a = bondStamp->getA();
690 int b = bondStamp->getB();
691 AtomStamp* atomA = getAtomStamp(a);
692 AtomStamp* atomB = getAtomStamp(b);
693 atomA->addBond(i);
694 atomA->addBondedAtom(b);
695 atomB->addBond(i);
696 atomB->addBondedAtom(a);
697 }
698 }
699
700 // Function Name: isBondInSameRigidBody
701 // Returns true is both atoms of the bond belong to the same rigid
702 // body, otherwise return false
703 bool FragmentStamp::isBondInSameRigidBody(BondStamp* bond) {
704 int rbA;
705 int rbB;
706 int consAtomA;
707 int consAtomB;
708
709 if (!isAtomInRigidBody(bond->getA(), rbA, consAtomA)) return false;
710
711 if (!isAtomInRigidBody(bond->getB(), rbB, consAtomB)) return false;
712
713 if (rbB == rbA)
714 return true;
715 else
716 return false;
717 }
718
719 // Function Name: isAtomInRigidBody
720 // Returns false if atom does not belong to a rigid body, otherwise
721 // returns true
722 bool FragmentStamp::isAtomInRigidBody(int atomIndex) {
723 return atom2Rigidbody[atomIndex] >= 0;
724 }
725
726 // Function Name: isAtomInRigidBody
727 // Returns false if atom does not belong to a rigid body otherwise
728 // returns true and sets whichRigidBody and consAtomIndex
729 // atomIndex : the index of atom in component
730 // whichRigidBody: the index of the rigidbody in the component
731 // consAtomIndex: the position the joint atom appears in the rigidbody's
732 // definition
733 bool FragmentStamp::isAtomInRigidBody(int atomIndex, int& whichRigidBody,
734 int& consAtomIndex) {
735 whichRigidBody = -1;
736 consAtomIndex = -1;
737
738 if (atom2Rigidbody[atomIndex] >= 0) {
739 whichRigidBody = atom2Rigidbody[atomIndex];
740 RigidBodyStamp* rbStamp = getRigidBodyStamp(whichRigidBody);
741 int numAtom = rbStamp->getNMembers();
742 for (int j = 0; j < numAtom; j++) {
743 if (rbStamp->getMemberAt(j) == atomIndex) {
744 consAtomIndex = j;
745 return true;
746 }
747 }
748 }
749
750 return false;
751 }
752
753 // Returns the position of joint atoms apearing in a rigidbody's definition
754 // For the time being, we will use the most inefficient algorithm,
755 // the complexity is O(N^2). We could improve the
756 // complexity to O(NlogN) by sorting the atom index in rigid body
757 // first
758 std::vector<std::pair<int, int>> FragmentStamp::getJointAtoms(int rb1,
759 int rb2) {
760 RigidBodyStamp* rbStamp1;
761 RigidBodyStamp* rbStamp2;
762 int natomInRb1;
763 int natomInRb2;
764 int atomIndex1;
765 int atomIndex2;
766 std::vector<std::pair<int, int>> jointAtomIndexPair;
767
768 rbStamp1 = this->getRigidBodyStamp(rb1);
769 natomInRb1 = rbStamp1->getNMembers();
770
771 rbStamp2 = this->getRigidBodyStamp(rb2);
772 natomInRb2 = rbStamp2->getNMembers();
773
774 for (int i = 0; i < natomInRb1; i++) {
775 atomIndex1 = rbStamp1->getMemberAt(i);
776
777 for (int j = 0; j < natomInRb2; j++) {
778 atomIndex2 = rbStamp2->getMemberAt(j);
779
780 if (atomIndex1 == atomIndex2) {
781 jointAtomIndexPair.push_back(std::make_pair(i, j));
782 break;
783 }
784 }
785 }
786
787 return jointAtomIndexPair;
788 }
789
790} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.