OpenMD 3.1
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
RigidBody.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 */
45
46#include <algorithm>
47#include <cmath>
48
49#include "utils/Constants.hpp"
50#include "utils/simError.h"
51
52namespace OpenMD {
53
54 RigidBody::RigidBody() :
55 StuntDouble(otRigidBody, &Snapshot::rigidbodyData), inertiaTensor_(0.0) {}
56
57 void RigidBody::setPrevA(const RotMat3x3d& a) {
58 ((snapshotMan_->getPrevSnapshot())->*storage_).aMat[localIndex_] = a;
59
60 for (unsigned int i = 0; i < atoms_.size(); ++i) {
61 if (atoms_[i]->isDirectional()) {
62 atoms_[i]->setPrevA(refOrients_[i].transpose() * a);
63 }
64 }
65 }
66
67 void RigidBody::setA(const RotMat3x3d& a) {
68 ((snapshotMan_->getCurrentSnapshot())->*storage_).aMat[localIndex_] = a;
69
70 for (unsigned int i = 0; i < atoms_.size(); ++i) {
71 if (atoms_[i]->isDirectional()) {
72 atoms_[i]->setA(refOrients_[i].transpose() * a);
73 }
74 }
75 }
76
77 void RigidBody::setA(const RotMat3x3d& a, int snapshotNo) {
78 ((snapshotMan_->getSnapshot(snapshotNo))->*storage_).aMat[localIndex_] = a;
79
80 for (unsigned int i = 0; i < atoms_.size(); ++i) {
81 if (atoms_[i]->isDirectional()) {
82 atoms_[i]->setA(refOrients_[i].transpose() * a, snapshotNo);
83 }
84 }
85 }
86
87 Mat3x3d RigidBody::getI() { return inertiaTensor_; }
88
89 std::vector<RealType> RigidBody::getGrad() {
90 std::vector<RealType> grad(6, 0.0);
91 Vector3d force;
92 Vector3d torque;
93 Vector3d myEuler;
94 RealType phi, theta;
95 RealType cphi, sphi, ctheta, stheta;
96 Vector3d ephi;
97 Vector3d etheta;
98 Vector3d epsi;
99
100 force = getFrc();
101 torque = getTrq();
102
103 myEuler = getA().toEulerAngles();
104
105 phi = myEuler[0];
106 theta = myEuler[1];
107
108 cphi = cos(phi);
109 sphi = sin(phi);
110 ctheta = cos(theta);
111 stheta = sin(theta);
112
113 if (fabs(stheta) < 1.0E-9) { stheta = 1.0E-9; }
114
115 ephi[0] = -sphi * ctheta / stheta;
116 ephi[1] = cphi * ctheta / stheta;
117 ephi[2] = 1.0;
118
119 etheta[0] = cphi;
120 etheta[1] = sphi;
121 etheta[2] = 0.0;
122
123 epsi[0] = sphi / stheta;
124 epsi[1] = -cphi / stheta;
125 epsi[2] = 0.0;
126
127 // gradient is equal to -force
128 for (int j = 0; j < 3; j++)
129 grad[j] = -force[j];
130
131 for (int j = 0; j < 3; j++) {
132 grad[3] -= torque[j] * ephi[j];
133 grad[4] -= torque[j] * etheta[j];
134 grad[5] -= torque[j] * epsi[j];
135 }
136
137 return grad;
138 }
139
140 void RigidBody::accept(BaseVisitor* v) { v->visit(this); }
141
142 /**@todo need modification */
143 void RigidBody::calcRefCoords() {
144 RealType mtmp;
145 Vector3d refCOM(0.0);
146 mass_ = 0.0;
147 for (std::size_t i = 0; i < atoms_.size(); ++i) {
148 mtmp = atoms_[i]->getMass();
149 mass_ += mtmp;
150 refCOM += refCoords_[i] * mtmp;
151 }
152 refCOM_ = refCOM / mass_;
153
154 // Next, move the origin of the reference coordinate system to the COM:
155 for (std::size_t i = 0; i < atoms_.size(); ++i) {
156 refCoords_[i] -= refCOM_;
157 }
158
159 // Moment of Inertia calculation
160 Mat3x3d Itmp(0.0);
161 for (std::size_t i = 0; i < atoms_.size(); i++) {
162 Mat3x3d IAtom(0.0);
163 mtmp = atoms_[i]->getMass();
164 IAtom -= outProduct(refCoords_[i], refCoords_[i]) * mtmp;
165 RealType r2 = refCoords_[i].lengthSquare();
166 IAtom(0, 0) += mtmp * r2;
167 IAtom(1, 1) += mtmp * r2;
168 IAtom(2, 2) += mtmp * r2;
169 Itmp += IAtom;
170
171 // project the inertial moment of directional atoms into this rigid body
172 if (atoms_[i]->isDirectional()) {
173 Itmp += refOrients_[i].transpose() * atoms_[i]->getI() * refOrients_[i];
174 }
175 }
176
177 // diagonalize
178 Vector3d evals;
179 Mat3x3d::diagonalize(Itmp, evals, sU_);
180
181 // zero out I and then fill the diagonals with the moments of inertia:
182 inertiaTensor_(0, 0) = evals[0];
183 inertiaTensor_(1, 1) = evals[1];
184 inertiaTensor_(2, 2) = evals[2];
185
186 int nLinearAxis = 0;
187 for (int i = 0; i < 3; i++) {
188 if (fabs(evals[i]) < OpenMD::epsilon) {
189 linear_ = true;
190 linearAxis_ = i;
191 ++nLinearAxis;
192 }
193 }
194
195 if (nLinearAxis > 1) {
196 snprintf(
197 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
198 "RigidBody error.\n"
199 "\tOpenMD found more than one axis in this rigid body with a "
200 "vanishing \n"
201 "\tmoment of inertia. This can happen in one of three ways:\n"
202 "\t 1) Only one atom was specified, or \n"
203 "\t 2) All atoms were specified at the same location, or\n"
204 "\t 3) The programmers did something stupid.\n"
205 "\tIt is silly to use a rigid body to describe this situation. Be "
206 "smarter.\n");
207 painCave.isFatal = 1;
208 simError();
209 }
210 }
211
212 void RigidBody::calcForcesAndTorques() {
213 Vector3d afrc;
214 Vector3d atrq;
215 Vector3d apos;
216 Vector3d rpos;
217 Vector3d frc(0.0);
218 Vector3d trq(0.0);
219 Vector3d ef(0.0);
220 Vector3d pos = this->getPos();
221 AtomType* atype;
222 int eCount = 0;
223
224 int sl =
225 ((snapshotMan_->getCurrentSnapshot())->*storage_).getStorageLayout();
226
227 for (unsigned int i = 0; i < atoms_.size(); i++) {
228 atype = atoms_[i]->getAtomType();
229
230 afrc = atoms_[i]->getFrc();
231 apos = atoms_[i]->getPos();
232 rpos = apos - pos;
233
234 frc += afrc;
235
236 trq[0] += rpos[1] * afrc[2] - rpos[2] * afrc[1];
237 trq[1] += rpos[2] * afrc[0] - rpos[0] * afrc[2];
238 trq[2] += rpos[0] * afrc[1] - rpos[1] * afrc[0];
239
240 // If the atom has a torque associated with it, then we also need to
241 // migrate the torques onto the center of mass:
242
243 if (atoms_[i]->isDirectional()) {
244 atrq = atoms_[i]->getTrq();
245 trq += atrq;
246 }
247
248 if ((sl & DataStorage::dslElectricField) && (atype->isElectrostatic())) {
249 ef += atoms_[i]->getElectricField();
250 eCount++;
251 }
252 }
253 addFrc(frc);
254 addTrq(trq);
255
256 if (sl & DataStorage::dslElectricField) {
257 ef /= eCount;
258 setElectricField(ef);
259 }
260 }
261
262 Mat3x3d RigidBody::calcForcesAndTorquesAndVirial() {
263 Vector3d afrc;
264 Vector3d atrq;
265 Vector3d apos;
266 Vector3d rpos;
267 Vector3d dfrc;
268 Vector3d frc(0.0);
269 Vector3d trq(0.0);
270 Vector3d ef(0.0);
271 AtomType* atype;
272 int eCount = 0;
273
274 Vector3d pos = this->getPos();
275 Mat3x3d tau_(0.0);
276
277 int sl =
278 ((snapshotMan_->getCurrentSnapshot())->*storage_).getStorageLayout();
279
280 for (unsigned int i = 0; i < atoms_.size(); i++) {
281 atype = atoms_[i]->getAtomType();
282
283 afrc = atoms_[i]->getFrc();
284 apos = atoms_[i]->getPos();
285 rpos = apos - pos;
286
287 frc += afrc;
288
289 trq[0] += rpos[1] * afrc[2] - rpos[2] * afrc[1];
290 trq[1] += rpos[2] * afrc[0] - rpos[0] * afrc[2];
291 trq[2] += rpos[0] * afrc[1] - rpos[1] * afrc[0];
292
293 // If the atom has a torque associated with it, then we also need to
294 // migrate the torques onto the center of mass:
295
296 if (atoms_[i]->isDirectional()) {
297 atrq = atoms_[i]->getTrq();
298 trq += atrq;
299 }
300
301 if ((sl & DataStorage::dslElectricField) && (atype->isElectrostatic())) {
302 ef += atoms_[i]->getElectricField();
303 eCount++;
304 }
305
306 tau_(0, 0) -= rpos[0] * afrc[0];
307 tau_(0, 1) -= rpos[0] * afrc[1];
308 tau_(0, 2) -= rpos[0] * afrc[2];
309 tau_(1, 0) -= rpos[1] * afrc[0];
310 tau_(1, 1) -= rpos[1] * afrc[1];
311 tau_(1, 2) -= rpos[1] * afrc[2];
312 tau_(2, 0) -= rpos[2] * afrc[0];
313 tau_(2, 1) -= rpos[2] * afrc[1];
314 tau_(2, 2) -= rpos[2] * afrc[2];
315 }
316 addFrc(frc);
317 addTrq(trq);
318
319 if (sl & DataStorage::dslElectricField) {
320 ef /= eCount;
321 setElectricField(ef);
322 }
323
324 return tau_;
325 }
326
327 void RigidBody::updateAtoms() {
328 unsigned int i;
329 Vector3d ref;
330 Vector3d apos;
331 DirectionalAtom* dAtom;
332 Vector3d pos = getPos();
333 RotMat3x3d a = getA();
334
335 for (i = 0; i < atoms_.size(); i++) {
336 ref = body2Lab(refCoords_[i]);
337
338 apos = pos + ref;
339
340 atoms_[i]->setPos(apos);
341
342 if (atoms_[i]->isDirectional()) {
343 dAtom = dynamic_cast<DirectionalAtom*>(atoms_[i]);
344 dAtom->setA(refOrients_[i].transpose() * a);
345 }
346 }
347 }
348
349 void RigidBody::updateAtoms(int frame) {
350 unsigned int i;
351 Vector3d ref;
352 Vector3d apos;
353 DirectionalAtom* dAtom;
354 Vector3d pos = getPos(frame);
355 RotMat3x3d a = getA(frame);
356
357 for (i = 0; i < atoms_.size(); i++) {
358 ref = body2Lab(refCoords_[i], frame);
359
360 apos = pos + ref;
361
362 atoms_[i]->setPos(apos, frame);
363
364 if (atoms_[i]->isDirectional()) {
365 dAtom = dynamic_cast<DirectionalAtom*>(atoms_[i]);
366 dAtom->setA(refOrients_[i].transpose() * a, frame);
367 }
368 }
369 }
370
371 void RigidBody::updateAtomVel() {
372 Mat3x3d skewMat;
373
374 Vector3d ji = getJ();
375 Mat3x3d I = getI();
376
377 skewMat(0, 0) = 0;
378 skewMat(0, 1) = ji[2] / I(2, 2);
379 skewMat(0, 2) = -ji[1] / I(1, 1);
380
381 skewMat(1, 0) = -ji[2] / I(2, 2);
382 skewMat(1, 1) = 0;
383 skewMat(1, 2) = ji[0] / I(0, 0);
384
385 skewMat(2, 0) = ji[1] / I(1, 1);
386 skewMat(2, 1) = -ji[0] / I(0, 0);
387 skewMat(2, 2) = 0;
388
389 Mat3x3d mat = (getA() * skewMat).transpose();
390 Vector3d rbVel = getVel();
391
392 Vector3d velRot;
393 for (unsigned int i = 0; i < refCoords_.size(); ++i) {
394 atoms_[i]->setVel(rbVel + mat * refCoords_[i]);
395 }
396 }
397
398 void RigidBody::updateAtomVel(int frame) {
399 Mat3x3d skewMat;
400 ;
401
402 Vector3d ji = getJ(frame);
403 Mat3x3d I = getI();
404
405 skewMat(0, 0) = 0;
406 skewMat(0, 1) = ji[2] / I(2, 2);
407 skewMat(0, 2) = -ji[1] / I(1, 1);
408
409 skewMat(1, 0) = -ji[2] / I(2, 2);
410 skewMat(1, 1) = 0;
411 skewMat(1, 2) = ji[0] / I(0, 0);
412
413 skewMat(2, 0) = ji[1] / I(1, 1);
414 skewMat(2, 1) = -ji[0] / I(0, 0);
415 skewMat(2, 2) = 0;
416
417 Mat3x3d mat = (getA(frame) * skewMat).transpose();
418 Vector3d rbVel = getVel(frame);
419
420 Vector3d velRot;
421 for (unsigned int i = 0; i < refCoords_.size(); ++i) {
422 atoms_[i]->setVel(rbVel + mat * refCoords_[i], frame);
423 }
424 }
425
426 bool RigidBody::getAtomPos(Vector3d& pos, unsigned int index) {
427 if (index < atoms_.size()) {
428 Vector3d ref = body2Lab(refCoords_[index]);
429 pos = getPos() + ref;
430 return true;
431 } else {
432 snprintf(
433 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
434 "%d is an invalid index. The current rigid body contans %lu atoms.\n",
435 index, atoms_.size());
436 painCave.isFatal = 0;
437 simError();
438 return false;
439 }
440 }
441
442 bool RigidBody::getAtomPos(Vector3d& pos, Atom* atom) {
443 std::vector<Atom*>::iterator i;
444 i = std::find(atoms_.begin(), atoms_.end(), atom);
445 if (i != atoms_.end()) {
446 // RigidBody class makes sure refCoords_ and atoms_ match each other
447 Vector3d ref = body2Lab(refCoords_[i - atoms_.begin()]);
448 pos = getPos() + ref;
449 return true;
450 } else {
451 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
452 "Atom %d does not belong to rigid body %d.\n",
453 atom->getGlobalIndex(), getGlobalIndex());
454 painCave.isFatal = 0;
455 simError();
456 return false;
457 }
458 }
459 bool RigidBody::getAtomVel(Vector3d& vel, unsigned int index) {
460 // velRot = $(A\cdot skew(I^{-1}j))^{T}refCoor$
461
462 if (index < atoms_.size()) {
463 Vector3d velRot;
464 Mat3x3d skewMat;
465 ;
466 Vector3d ref = refCoords_[index];
467 Vector3d ji = getJ();
468 Mat3x3d I = getI();
469
470 skewMat(0, 0) = 0;
471 skewMat(0, 1) = ji[2] / I(2, 2);
472 skewMat(0, 2) = -ji[1] / I(1, 1);
473
474 skewMat(1, 0) = -ji[2] / I(2, 2);
475 skewMat(1, 1) = 0;
476 skewMat(1, 2) = ji[0] / I(0, 0);
477
478 skewMat(2, 0) = ji[1] / I(1, 1);
479 skewMat(2, 1) = -ji[0] / I(0, 0);
480 skewMat(2, 2) = 0;
481
482 velRot = (getA() * skewMat).transpose() * ref;
483
484 vel = getVel() + velRot;
485 return true;
486
487 } else {
488 snprintf(
489 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
490 "Index %d is an invalid index, the current rigid body contains %lu "
491 "atoms.\n",
492 index, atoms_.size());
493 painCave.isFatal = 0;
494 simError();
495 return false;
496 }
497 }
498
499 bool RigidBody::getAtomVel(Vector3d& vel, Atom* atom) {
500 std::vector<Atom*>::iterator i;
501 i = std::find(atoms_.begin(), atoms_.end(), atom);
502 if (i != atoms_.end()) {
503 return getAtomVel(vel, i - atoms_.begin());
504 } else {
505 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
506 "Atom %d does not belong to rigid body %d.\n",
507 atom->getGlobalIndex(), getGlobalIndex());
508 painCave.isFatal = 0;
509 simError();
510 return false;
511 }
512 }
513
514 bool RigidBody::getAtomRefCoor(Vector3d& coor, unsigned int index) {
515 if (index < atoms_.size()) {
516 coor = refCoords_[index];
517 return true;
518 } else {
519 snprintf(
520 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
521 "Index %d is an invalid index, the current rigid body contains %lu "
522 "atoms.\n",
523 index, atoms_.size());
524 painCave.isFatal = 0;
525 simError();
526 return false;
527 }
528 }
529
530 bool RigidBody::getAtomRefCoor(Vector3d& coor, Atom* atom) {
531 std::vector<Atom*>::iterator i;
532 i = std::find(atoms_.begin(), atoms_.end(), atom);
533 if (i != atoms_.end()) {
534 // RigidBody class makes sure refCoords_ and atoms_ match each other
535 coor = refCoords_[i - atoms_.begin()];
536 return true;
537 } else {
538 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
539 "Atom %d does not belong to rigid body %d.\n",
540 atom->getGlobalIndex(), getGlobalIndex());
541 painCave.isFatal = 0;
542 simError();
543 return false;
544 }
545 }
546
547 void RigidBody::addAtom(Atom* at, AtomStamp* ats) {
548 Vector3d coords;
549 Vector3d euler;
550
551 atoms_.push_back(at);
552
553 if (!ats->havePosition()) {
554 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
555 "RigidBody error.\n"
556 "\tAtom %s does not have a position specified.\n"
557 "\tThis means RigidBody cannot set up reference coordinates.\n",
558 ats->getType().c_str());
559 painCave.isFatal = 1;
560 simError();
561 }
562
563 coords[0] = ats->getPosX();
564 coords[1] = ats->getPosY();
565 coords[2] = ats->getPosZ();
566
567 refCoords_.push_back(coords);
568
569 RotMat3x3d identMat = RotMat3x3d::identity();
570
571 if (at->isDirectional()) {
572 if (!ats->haveOrientation()) {
573 snprintf(
574 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
575 "RigidBody error.\n"
576 "\tAtom %s does not have an orientation specified.\n"
577 "\tThis means RigidBody cannot set up reference orientations.\n",
578 ats->getType().c_str());
579 painCave.isFatal = 1;
580 simError();
581 }
582
583 euler[0] = ats->getEulerPhi() * Constants::PI / 180.0;
584 euler[1] = ats->getEulerTheta() * Constants::PI / 180.0;
585 euler[2] = ats->getEulerPsi() * Constants::PI / 180.0;
586
587 RotMat3x3d Atmp(euler);
588 refOrients_.push_back(Atmp);
589
590 } else {
591 refOrients_.push_back(identMat);
592 }
593 }
594
595} // namespace OpenMD
AtomType is what OpenMD looks to for unchanging data about an atom.
Definition AtomType.hpp:66
virtual void setA(const RotMat3x3d &a)
Sets the current rotation matrix of this stuntdouble.
int getGlobalIndex()
Returns the global index of this stuntDouble.
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.