OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
SelectionManager.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
45#include "selection/SelectionManager.hpp"
46
47#ifdef IS_MPI
48#include <mpi.h>
49#endif
50
51#include "brains/SimInfo.hpp"
52
53namespace OpenMD {
54 SelectionManager::SelectionManager(SimInfo* info) : info_(info) {
55 nObjects_.push_back(info_->getNGlobalAtoms() +
56 info_->getNGlobalRigidBodies());
57 nObjects_.push_back(info_->getNGlobalBonds());
58 nObjects_.push_back(info_->getNGlobalBends());
59 nObjects_.push_back(info_->getNGlobalTorsions());
60 nObjects_.push_back(info_->getNGlobalInversions());
61 nObjects_.push_back(info_->getNGlobalMolecules());
62
63 stuntdoubles_.resize(nObjects_[STUNTDOUBLE]);
64 bonds_.resize(nObjects_[BOND]);
65 bends_.resize(nObjects_[BEND]);
66 torsions_.resize(nObjects_[TORSION]);
67 inversions_.resize(nObjects_[INVERSION]);
68 molecules_.resize(nObjects_[MOLECULE]);
69
70 ss_.resize(nObjects_);
71
72 SimInfo::MoleculeIterator mi;
73 Molecule::AtomIterator ai;
74 Molecule::RigidBodyIterator rbIter;
75 Molecule::BondIterator bondIter;
76 Molecule::BendIterator bendIter;
77 Molecule::TorsionIterator torsionIter;
78 Molecule::InversionIterator inversionIter;
79
80 Molecule* mol;
81 Atom* atom;
82 RigidBody* rb;
83 Bond* bond;
84 Bend* bend;
85 Torsion* torsion;
86 Inversion* inversion;
87
88 for (mol = info_->beginMolecule(mi); mol != NULL;
89 mol = info_->nextMolecule(mi)) {
90 molecules_[mol->getGlobalIndex()] = mol;
91
92 for (atom = mol->beginAtom(ai); atom != NULL; atom = mol->nextAtom(ai)) {
93 stuntdoubles_[atom->getGlobalIndex()] = atom;
94 }
95 for (rb = mol->beginRigidBody(rbIter); rb != NULL;
96 rb = mol->nextRigidBody(rbIter)) {
97 stuntdoubles_[rb->getGlobalIndex()] = rb;
98 }
99 for (bond = mol->beginBond(bondIter); bond != NULL;
100 bond = mol->nextBond(bondIter)) {
101 bonds_[bond->getGlobalIndex()] = bond;
102 }
103 for (bend = mol->beginBend(bendIter); bend != NULL;
104 bend = mol->nextBend(bendIter)) {
105 bends_[bend->getGlobalIndex()] = bend;
106 }
107 for (torsion = mol->beginTorsion(torsionIter); torsion != NULL;
108 torsion = mol->nextTorsion(torsionIter)) {
109 torsions_[torsion->getGlobalIndex()] = torsion;
110 }
111 for (inversion = mol->beginInversion(inversionIter); inversion != NULL;
112 inversion = mol->nextInversion(inversionIter)) {
113 inversions_[inversion->getGlobalIndex()] = inversion;
114 }
115 }
116 }
117
118 StuntDouble* SelectionManager::beginSelected(int& i) {
119#ifdef IS_MPI
120 i = 0;
121 while (i < static_cast<int>(ss_.bitsets_[STUNTDOUBLE].size())) {
122 if (ss_.bitsets_[STUNTDOUBLE][i]) {
123 // check that this processor owns this stuntdouble
124 if (stuntdoubles_[i] != NULL) return stuntdoubles_[i];
125 }
126 ++i;
127 }
128 return NULL;
129#else
130 i = ss_.bitsets_[STUNTDOUBLE].firstOnBit();
131 return i == -1 ? NULL : stuntdoubles_[i];
132#endif
133 }
134
135 StuntDouble* SelectionManager::nextSelected(int& i) {
136#ifdef IS_MPI
137 ++i;
138 while (i < static_cast<int>(ss_.bitsets_[STUNTDOUBLE].size())) {
139 if (ss_.bitsets_[STUNTDOUBLE][i]) {
140 // check that this processor owns this stuntdouble
141 if (stuntdoubles_[i] != NULL) return stuntdoubles_[i];
142 }
143 ++i;
144 }
145 return NULL;
146#else
147 i = ss_.bitsets_[STUNTDOUBLE].nextOnBit(i);
148 return i == -1 ? NULL : stuntdoubles_[i];
149#endif
150 }
151
152 StuntDouble* SelectionManager::beginUnselected(int& i) {
153#ifdef IS_MPI
154 i = 0;
155 while (i < static_cast<int>(ss_.bitsets_[STUNTDOUBLE].size())) {
156 if (!ss_.bitsets_[STUNTDOUBLE][i]) {
157 // check that this processor owns this stuntdouble
158 if (stuntdoubles_[i] != NULL) return stuntdoubles_[i];
159 }
160 ++i;
161 }
162 return NULL;
163#else
164 i = ss_.bitsets_[STUNTDOUBLE].firstOffBit();
165 return i == -1 ? NULL : stuntdoubles_[i];
166#endif
167 }
168
169 StuntDouble* SelectionManager::nextUnselected(int& i) {
170#ifdef IS_MPI
171 ++i;
172 while (i < static_cast<int>(ss_.bitsets_[STUNTDOUBLE].size())) {
173 if (!ss_.bitsets_[STUNTDOUBLE][i]) {
174 // check that this processor owns this stuntdouble
175 if (stuntdoubles_[i] != NULL) return stuntdoubles_[i];
176 }
177 ++i;
178 }
179 return NULL;
180#else
181 i = ss_.bitsets_[STUNTDOUBLE].nextOffBit(i);
182 return i == -1 ? NULL : stuntdoubles_[i];
183#endif
184 }
185
186 Bond* SelectionManager::beginSelectedBond(int& i) {
187#ifdef IS_MPI
188 i = 0;
189 while (i < static_cast<int>(ss_.bitsets_[BOND].size())) {
190 if (ss_.bitsets_[BOND][i]) {
191 // check that this processor owns this bond
192 if (bonds_[i] != NULL) return bonds_[i];
193 }
194 ++i;
195 }
196 return NULL;
197#else
198 i = ss_.bitsets_[BOND].firstOnBit();
199 return i == -1 ? NULL : bonds_[i];
200#endif
201 }
202
203 Bond* SelectionManager::nextSelectedBond(int& i) {
204#ifdef IS_MPI
205 ++i;
206 while (i < static_cast<int>(ss_.bitsets_[BOND].size())) {
207 if (ss_.bitsets_[BOND][i]) {
208 // check that this processor owns this bond
209 if (bonds_[i] != NULL) return bonds_[i];
210 }
211 ++i;
212 }
213 return NULL;
214#else
215 i = ss_.bitsets_[BOND].nextOnBit(i);
216 return i == -1 ? NULL : bonds_[i];
217#endif
218 }
219
220 Bond* SelectionManager::beginUnselectedBond(int& i) {
221#ifdef IS_MPI
222 i = 0;
223 while (i < static_cast<int>(ss_.bitsets_[BOND].size())) {
224 if (!ss_.bitsets_[BOND][i]) {
225 // check that this processor owns this bond
226 if (bonds_[i] != NULL) return bonds_[i];
227 }
228 ++i;
229 }
230 return NULL;
231#else
232 i = ss_.bitsets_[BOND].firstOffBit();
233 return i == -1 ? NULL : bonds_[i];
234#endif
235 }
236
237 Bond* SelectionManager::nextUnselectedBond(int& i) {
238#ifdef IS_MPI
239 ++i;
240 while (i < static_cast<int>(ss_.bitsets_[BOND].size())) {
241 if (!ss_.bitsets_[BOND][i]) {
242 // check that this processor owns this bond
243 if (bonds_[i] != NULL) return bonds_[i];
244 }
245 ++i;
246 }
247 return NULL;
248#else
249 i = ss_.bitsets_[BOND].nextOffBit(i);
250 return i == -1 ? NULL : bonds_[i];
251#endif
252 }
253
254 Bend* SelectionManager::beginSelectedBend(int& i) {
255#ifdef IS_MPI
256 i = 0;
257 while (i < static_cast<int>(ss_.bitsets_[BEND].size())) {
258 if (ss_.bitsets_[BEND][i]) {
259 // check that this processor owns this bend
260 if (bends_[i] != NULL) return bends_[i];
261 }
262 ++i;
263 }
264 return NULL;
265#else
266 i = ss_.bitsets_[BEND].firstOnBit();
267 return i == -1 ? NULL : bends_[i];
268#endif
269 }
270
271 Bend* SelectionManager::nextSelectedBend(int& i) {
272#ifdef IS_MPI
273 ++i;
274 while (i < static_cast<int>(ss_.bitsets_[BEND].size())) {
275 if (ss_.bitsets_[BEND][i]) {
276 // check that this processor owns this bend
277 if (bends_[i] != NULL) return bends_[i];
278 }
279 ++i;
280 }
281 return NULL;
282#else
283 i = ss_.bitsets_[BEND].nextOnBit(i);
284 return i == -1 ? NULL : bends_[i];
285#endif
286 }
287
288 Bend* SelectionManager::beginUnselectedBend(int& i) {
289#ifdef IS_MPI
290 i = 0;
291 while (i < static_cast<int>(ss_.bitsets_[BEND].size())) {
292 if (!ss_.bitsets_[BEND][i]) {
293 // check that this processor owns this bend
294 if (bends_[i] != NULL) return bends_[i];
295 }
296 ++i;
297 }
298 return NULL;
299#else
300 i = ss_.bitsets_[BEND].firstOffBit();
301 return i == -1 ? NULL : bends_[i];
302#endif
303 }
304
305 Bend* SelectionManager::nextUnselectedBend(int& i) {
306#ifdef IS_MPI
307 ++i;
308 while (i < static_cast<int>(ss_.bitsets_[BEND].size())) {
309 if (!ss_.bitsets_[BEND][i]) {
310 // check that this processor owns this bend
311 if (bends_[i] != NULL) return bends_[i];
312 }
313 ++i;
314 }
315 return NULL;
316#else
317 i = ss_.bitsets_[BEND].nextOffBit(i);
318 return i == -1 ? NULL : bends_[i];
319#endif
320 }
321
322 Torsion* SelectionManager::beginSelectedTorsion(int& i) {
323#ifdef IS_MPI
324 i = 0;
325 while (i < static_cast<int>(ss_.bitsets_[TORSION].size())) {
326 if (ss_.bitsets_[TORSION][i]) {
327 // check that this processor owns this torsion
328 if (torsions_[i] != NULL) return torsions_[i];
329 }
330 ++i;
331 }
332 return NULL;
333#else
334 i = ss_.bitsets_[TORSION].firstOnBit();
335 return i == -1 ? NULL : torsions_[i];
336#endif
337 }
338
339 Torsion* SelectionManager::nextSelectedTorsion(int& i) {
340#ifdef IS_MPI
341 ++i;
342 while (i < static_cast<int>(ss_.bitsets_[TORSION].size())) {
343 if (ss_.bitsets_[TORSION][i]) {
344 // check that this processor owns this torsion
345 if (torsions_[i] != NULL) return torsions_[i];
346 }
347 ++i;
348 }
349 return NULL;
350#else
351 i = ss_.bitsets_[TORSION].nextOnBit(i);
352 return i == -1 ? NULL : torsions_[i];
353#endif
354 }
355
356 Torsion* SelectionManager::beginUnselectedTorsion(int& i) {
357#ifdef IS_MPI
358 i = 0;
359 while (i < static_cast<int>(ss_.bitsets_[TORSION].size())) {
360 if (!ss_.bitsets_[TORSION][i]) {
361 // check that this processor owns this torsion
362 if (torsions_[i] != NULL) return torsions_[i];
363 }
364 ++i;
365 }
366 return NULL;
367#else
368 i = ss_.bitsets_[TORSION].firstOffBit();
369 return i == -1 ? NULL : torsions_[i];
370#endif
371 }
372
373 Torsion* SelectionManager::nextUnselectedTorsion(int& i) {
374#ifdef IS_MPI
375 ++i;
376 while (i < static_cast<int>(ss_.bitsets_[TORSION].size())) {
377 if (!ss_.bitsets_[TORSION][i]) {
378 // check that this processor owns this torsion
379 if (torsions_[i] != NULL) return torsions_[i];
380 }
381 ++i;
382 }
383 return NULL;
384#else
385 i = ss_.bitsets_[TORSION].nextOffBit(i);
386 return i == -1 ? NULL : torsions_[i];
387#endif
388 }
389
390 Inversion* SelectionManager::beginSelectedInversion(int& i) {
391#ifdef IS_MPI
392 i = 0;
393 while (i < static_cast<int>(ss_.bitsets_[INVERSION].size())) {
394 if (ss_.bitsets_[INVERSION][i]) {
395 // check that this processor owns this inversion
396 if (inversions_[i] != NULL) return inversions_[i];
397 }
398 ++i;
399 }
400 return NULL;
401#else
402 i = ss_.bitsets_[INVERSION].firstOnBit();
403 return i == -1 ? NULL : inversions_[i];
404#endif
405 }
406
407 Inversion* SelectionManager::nextSelectedInversion(int& i) {
408#ifdef IS_MPI
409 ++i;
410 while (i < static_cast<int>(ss_.bitsets_[INVERSION].size())) {
411 if (ss_.bitsets_[INVERSION][i]) {
412 // check that this processor owns this inversion
413 if (inversions_[i] != NULL) return inversions_[i];
414 }
415 ++i;
416 }
417 return NULL;
418#else
419 i = ss_.bitsets_[INVERSION].nextOnBit(i);
420 return i == -1 ? NULL : inversions_[i];
421#endif
422 }
423
424 Inversion* SelectionManager::beginUnselectedInversion(int& i) {
425#ifdef IS_MPI
426 i = 0;
427 while (i < static_cast<int>(ss_.bitsets_[INVERSION].size())) {
428 if (!ss_.bitsets_[INVERSION][i]) {
429 // check that this processor owns this inversion
430 if (inversions_[i] != NULL) return inversions_[i];
431 }
432 ++i;
433 }
434 return NULL;
435#else
436 i = ss_.bitsets_[INVERSION].firstOffBit();
437 return i == -1 ? NULL : inversions_[i];
438#endif
439 }
440
441 Inversion* SelectionManager::nextUnselectedInversion(int& i) {
442#ifdef IS_MPI
443 ++i;
444 while (i < static_cast<int>(ss_.bitsets_[INVERSION].size())) {
445 if (!ss_.bitsets_[INVERSION][i]) {
446 // check that this processor owns this inversion
447 if (inversions_[i] != NULL) return inversions_[i];
448 }
449 ++i;
450 }
451 return NULL;
452#else
453 i = ss_.bitsets_[INVERSION].nextOffBit(i);
454 return i == -1 ? NULL : inversions_[i];
455#endif
456 }
457
458 Molecule* SelectionManager::beginSelectedMolecule(int& i) {
459#ifdef IS_MPI
460 i = 0;
461 while (i < static_cast<int>(ss_.bitsets_[MOLECULE].size())) {
462 if (ss_.bitsets_[MOLECULE][i]) {
463 // check that this processor owns this molecule
464 if (molecules_[i] != NULL) return molecules_[i];
465 }
466 ++i;
467 }
468 return NULL;
469#else
470 i = ss_.bitsets_[MOLECULE].firstOnBit();
471 return i == -1 ? NULL : molecules_[i];
472#endif
473 }
474
475 Molecule* SelectionManager::nextSelectedMolecule(int& i) {
476#ifdef IS_MPI
477 ++i;
478 while (i < static_cast<int>(ss_.bitsets_[MOLECULE].size())) {
479 if (ss_.bitsets_[MOLECULE][i]) {
480 // check that this processor owns this molecule
481 if (molecules_[i] != NULL) return molecules_[i];
482 }
483 ++i;
484 }
485 return NULL;
486#else
487 i = ss_.bitsets_[MOLECULE].nextOnBit(i);
488 return i == -1 ? NULL : molecules_[i];
489#endif
490 }
491
492 Molecule* SelectionManager::beginUnselectedMolecule(int& i) {
493#ifdef IS_MPI
494 i = 0;
495 while (i < static_cast<int>(ss_.bitsets_[MOLECULE].size())) {
496 if (!ss_.bitsets_[MOLECULE][i]) {
497 // check that this processor owns this molecule
498 if (molecules_[i] != NULL) return molecules_[i];
499 }
500 ++i;
501 }
502 return NULL;
503#else
504 i = ss_.bitsets_[MOLECULE].firstOffBit();
505 return i == -1 ? NULL : molecules_[i];
506#endif
507 }
508
509 Molecule* SelectionManager::nextUnselectedMolecule(int& i) {
510#ifdef IS_MPI
511 ++i;
512 while (i < static_cast<int>(ss_.bitsets_[MOLECULE].size())) {
513 if (!ss_.bitsets_[MOLECULE][i]) {
514 // check that this processor owns this molecule
515 if (molecules_[i] != NULL) return molecules_[i];
516 }
517 ++i;
518 }
519 return NULL;
520#else
521 i = ss_.bitsets_[MOLECULE].nextOffBit(i);
522 return i == -1 ? NULL : molecules_[i];
523#endif
524 }
525
526 Molecule* SelectionManager::nthSelectedMolecule(int& n) {
527 int i;
528#ifdef IS_MPI
529 i = ss_.bitsets_[MOLECULE].nthOnBit(n);
530 if (i == -1) return NULL;
531 // check that this processor owns this molecule
532 if (molecules_[i] != NULL) return molecules_[i];
533 return NULL;
534#else
535 i = ss_.bitsets_[MOLECULE].nthOnBit(n);
536 return i == -1 ? NULL : molecules_[i];
537#endif
538 }
539
540 /**
541 * getSelectedAtomTypes
542 *
543 * Returns an STL set of AtomType* that are actually selected.
544 * Must query all processors to assemble this information.
545 *
546 */
547 AtomTypeSet SelectionManager::getSelectedAtomTypes() {
548 AtomTypeSet atomTypes;
549
550 for (size_t i = 0; i < ss_.bitsets_[STUNTDOUBLE].size(); ++i) {
551 if (ss_.bitsets_[STUNTDOUBLE][i]) {
552 // check that this processor owns this stuntdouble
553 if (stuntdoubles_[i] != NULL) {
554 if (stuntdoubles_[i]->isAtom()) {
555 Atom* atom = static_cast<Atom*>(stuntdoubles_[i]);
556 atomTypes.insert(atom->getAtomType());
557 }
558 }
559 }
560 }
561
562#ifdef IS_MPI
563 // loop over the found atom types on this processor, and add their
564 // numerical idents to a vector:
565
566 std::vector<int> foundTypes;
567 AtomTypeSet::iterator i;
568 for (i = atomTypes.begin(); i != atomTypes.end(); ++i)
569 foundTypes.push_back((*i)->getIdent());
570
571 // count_local holds the number of found types on this processor
572 int count_local = foundTypes.size();
573
574 int nproc;
575 MPI_Comm_size(MPI_COMM_WORLD, &nproc);
576
577 // we need arrays to hold the counts and displacement vectors for
578 // all processors
579 std::vector<int> counts(nproc, 0);
580 std::vector<int> disps(nproc, 0);
581
582 // fill the counts array
583 MPI_Allgather(&count_local, 1, MPI_INT, &counts[0], 1, MPI_INT,
584 MPI_COMM_WORLD);
585
586 // use the processor counts to compute the displacement array
587 disps[0] = 0;
588 int totalCount = counts[0];
589 for (int iproc = 1; iproc < nproc; iproc++) {
590 disps[iproc] = disps[iproc - 1] + counts[iproc - 1];
591 totalCount += counts[iproc];
592 }
593
594 // we need a (possibly redundant) set of all found types:
595 std::vector<int> ftGlobal(totalCount);
596
597 // now spray out the foundTypes to all the other processors:
598 MPI_Allgatherv(&foundTypes[0], count_local, MPI_INT, &ftGlobal[0],
599 &counts[0], &disps[0], MPI_INT, MPI_COMM_WORLD);
600
601 std::vector<int>::iterator j;
602
603 // foundIdents is a stl set, so inserting an already found ident
604 // will have no effect.
605 std::set<int> foundIdents;
606
607 for (j = ftGlobal.begin(); j != ftGlobal.end(); ++j)
608 foundIdents.insert((*j));
609
610 // now iterate over the foundIdents and get the actual atom types
611 // that correspond to these:
612 ForceField* forceField_ = info_->getForceField();
613 std::set<int>::iterator it;
614 for (it = foundIdents.begin(); it != foundIdents.end(); ++it)
615 atomTypes.insert(forceField_->getAtomType((*it)));
616#endif
617
618 return atomTypes;
619 }
620
621 SelectionManager operator|(const SelectionManager& sman1,
622 const SelectionManager& sman2) {
623 SelectionManager result(sman1);
624 result |= sman2;
625 return result;
626 }
627
628 SelectionManager operator&(const SelectionManager& sman1,
629 const SelectionManager& sman2) {
630 SelectionManager result(sman1);
631 result &= sman2;
632 return result;
633 }
634
635 SelectionManager operator^(const SelectionManager& sman1,
636 const SelectionManager& sman2) {
637 SelectionManager result(sman1);
638 result ^= sman2;
639 return result;
640 }
641
642 SelectionManager operator-(const SelectionManager& sman1,
643 const SelectionManager& sman2) {
644 SelectionManager result(sman1);
645 result -= sman2;
646 return result;
647 }
648
649} // namespace OpenMD
AtomType * getAtomType(const std::string &at)
getAtomType by string
"Don't move, or you're dead! Stand up! Captain, we've got them!"
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
DynamicRectMatrix< Real > operator-(const DynamicRectMatrix< Real > &m)
Negate the value of every element of this matrix.
@ STUNTDOUBLE
StuntDoubles (Atoms & RigidBodies)