ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE/libmdtools/RigidBody.cpp
Revision: 1100
Committed: Mon Apr 12 21:02:01 2004 UTC (20 years, 2 months ago) by gezelter
File size: 13598 byte(s)
Log Message:
Adding RigidBody code

File Contents

# Content
1 #include <math.h>
2 #include "RigidBody.hpp"
3 #include "DirectionalAtom.hpp"
4 #include "simError.h"
5 #include "MatVec3.h"
6
7 RigidBody::RigidBody() : StuntDouble() {
8 objType = OT_RIGIDBODY;
9 com_good = false;
10 precalc_done = false;
11 }
12
13 RigidBody::~RigidBody() {
14 }
15
16 void RigidBody::addAtom(Atom* at, AtomStamp* ats) {
17
18 vec3 coords;
19 vec3 euler;
20 mat3x3 Atmp;
21
22 myAtoms.push_back(at);
23
24 if( !ats->havePosition() ){
25 sprintf( painCave.errMsg,
26 "RigidBody error.\n"
27 "\tAtom %s does not have a position specified.\n"
28 "\tThis means RigidBody cannot set up reference coordinates.\n",
29 ats->getType() );
30 painCave.isFatal = 1;
31 simError();
32 }
33
34 coords[0] = ats->getPosX();
35 coords[1] = ats->getPosY();
36 coords[2] = ats->getPosZ();
37
38 refCoords.push_back(coords);
39
40 if (at->isDirectional()) {
41
42 if( !ats->haveOrientation() ){
43 sprintf( painCave.errMsg,
44 "RigidBody error.\n"
45 "\tAtom %s does not have an orientation specified.\n"
46 "\tThis means RigidBody cannot set up reference orientations.\n",
47 ats->getType() );
48 painCave.isFatal = 1;
49 simError();
50 }
51
52 euler[0] = ats->getEulerPhi();
53 euler[1] = ats->getEulerTheta();
54 euler[2] = ats->getEulerPsi();
55
56 doEulerToRotMat(euler, Atmp);
57
58 refOrients.push_back(Atmp);
59
60 }
61 }
62
63 void RigidBody::getPos(double theP[3]){
64 for (int i = 0; i < 3 ; i++)
65 theP[i] = pos[i];
66 }
67
68 void RigidBody::setPos(double theP[3]){
69 for (int i = 0; i < 3 ; i++)
70 pos[i] = theP[i];
71 }
72
73 void RigidBody::getVel(double theV[3]){
74 for (int i = 0; i < 3 ; i++)
75 theV[i] = vel[i];
76 }
77
78 void RigidBody::setVel(double theV[3]){
79 for (int i = 0; i < 3 ; i++)
80 vel[i] = theV[i];
81 }
82
83 void RigidBody::getFrc(double theF[3]){
84 for (int i = 0; i < 3 ; i++)
85 theF[i] = frc[i];
86 }
87
88 void RigidBody::addFrc(double theF[3]){
89 for (int i = 0; i < 3 ; i++)
90 frc[i] += theF[i];
91 }
92
93 void RigidBody::zeroForces() {
94
95 for (int i = 0; i < 3; i++) {
96 frc[i] = 0.0;
97 trq[i] = 0.0;
98 }
99
100 forces_good = false;
101
102 }
103
104 void RigidBody::setEulerAngles( double phi, double theta, double psi ){
105
106 A[0][0] = (cos(phi) * cos(psi)) - (sin(phi) * cos(theta) * sin(psi));
107 A[0][1] = (sin(phi) * cos(psi)) + (cos(phi) * cos(theta) * sin(psi));
108 A[0][2] = sin(theta) * sin(psi);
109
110 A[1][0] = -(cos(phi) * sin(psi)) - (sin(phi) * cos(theta) * cos(psi));
111 A[1][1] = -(sin(phi) * sin(psi)) + (cos(phi) * cos(theta) * cos(psi));
112 A[1][2] = sin(theta) * cos(psi);
113
114 A[2][0] = sin(phi) * sin(theta);
115 A[2][1] = -cos(phi) * sin(theta);
116 A[2][2] = cos(theta);
117
118 }
119
120 void RigidBody::getQ( double q[4] ){
121
122 double t, s;
123 double ad1, ad2, ad3;
124
125 t = A[0][0] + A[1][1] + A[2][2] + 1.0;
126 if( t > 0.0 ){
127
128 s = 0.5 / sqrt( t );
129 q[0] = 0.25 / s;
130 q[1] = (A[1][2] - A[2][1]) * s;
131 q[2] = (A[2][0] - A[0][2]) * s;
132 q[3] = (A[0][1] - A[1][0]) * s;
133 }
134 else{
135
136 ad1 = fabs( A[0][0] );
137 ad2 = fabs( A[1][1] );
138 ad3 = fabs( A[2][2] );
139
140 if( ad1 >= ad2 && ad1 >= ad3 ){
141
142 s = 2.0 * sqrt( 1.0 + A[0][0] - A[1][1] - A[2][2] );
143 q[0] = (A[1][2] + A[2][1]) / s;
144 q[1] = 0.5 / s;
145 q[2] = (A[0][1] + A[1][0]) / s;
146 q[3] = (A[0][2] + A[2][0]) / s;
147 }
148 else if( ad2 >= ad1 && ad2 >= ad3 ){
149
150 s = sqrt( 1.0 + A[1][1] - A[0][0] - A[2][2] ) * 2.0;
151 q[0] = (A[0][2] + A[2][0]) / s;
152 q[1] = (A[0][1] + A[1][0]) / s;
153 q[2] = 0.5 / s;
154 q[3] = (A[1][2] + A[2][1]) / s;
155 }
156 else{
157
158 s = sqrt( 1.0 + A[2][2] - A[0][0] - A[1][1] ) * 2.0;
159 q[0] = (A[0][1] + A[1][0]) / s;
160 q[1] = (A[0][2] + A[2][0]) / s;
161 q[2] = (A[1][2] + A[2][1]) / s;
162 q[3] = 0.5 / s;
163 }
164 }
165 }
166
167 void RigidBody::setQ( double the_q[4] ){
168
169 double q0Sqr, q1Sqr, q2Sqr, q3Sqr;
170
171 q0Sqr = the_q[0] * the_q[0];
172 q1Sqr = the_q[1] * the_q[1];
173 q2Sqr = the_q[2] * the_q[2];
174 q3Sqr = the_q[3] * the_q[3];
175
176 A[0][0] = q0Sqr + q1Sqr - q2Sqr - q3Sqr;
177 A[0][1] = 2.0 * ( the_q[1] * the_q[2] + the_q[0] * the_q[3] );
178 A[0][2] = 2.0 * ( the_q[1] * the_q[3] - the_q[0] * the_q[2] );
179
180 A[1][0] = 2.0 * ( the_q[1] * the_q[2] - the_q[0] * the_q[3] );
181 A[1][1] = q0Sqr - q1Sqr + q2Sqr - q3Sqr;
182 A[1][2] = 2.0 * ( the_q[2] * the_q[3] + the_q[0] * the_q[1] );
183
184 A[2][0] = 2.0 * ( the_q[1] * the_q[3] + the_q[0] * the_q[2] );
185 A[2][1] = 2.0 * ( the_q[2] * the_q[3] - the_q[0] * the_q[1] );
186 A[2][2] = q0Sqr - q1Sqr -q2Sqr +q3Sqr;
187
188 }
189
190 void RigidBody::getA( double the_A[3][3] ){
191
192 for (int i = 0; i < 3; i++)
193 for (int j = 0; j < 3; j++)
194 the_A[i][j] = the_A[i][j];
195
196 }
197
198 void RigidBody::setA( double the_A[3][3] ){
199
200 for (int i = 0; i < 3; i++)
201 for (int j = 0; j < 3; j++)
202 A[i][j] = the_A[i][j];
203
204 }
205
206 void RigidBody::getJ( double theJ[3] ){
207
208 for (int i = 0; i < 3; i++)
209 theJ[i] = ji[i];
210
211 }
212
213 void RigidBody::setJ( double theJ[3] ){
214
215 for (int i = 0; i < 3; i++)
216 ji[i] = theJ[i];
217
218 }
219
220 void RigidBody::getTrq(double theT[3]){
221 for (int i = 0; i < 3 ; i++)
222 theT[i] = trq[i];
223 }
224
225 void RigidBody::addTrq(double theT[3]){
226 for (int i = 0; i < 3 ; i++)
227 trq[i] += theT[i];
228 }
229
230 void RigidBody::getI( double the_I[3][3] ){
231
232 if (precalc_done) {
233
234 for (int i = 0; i < 3; i++)
235 for (int j = 0; j < 3; j++)
236 the_I[i][j] = I[i][j];
237
238 } else {
239
240 }
241 }
242
243 void RigidBody::lab2Body( double r[3] ){
244
245 double rl[3]; // the lab frame vector
246
247 rl[0] = r[0];
248 rl[1] = r[1];
249 rl[2] = r[2];
250
251 r[0] = (A[0][0] * rl[0]) + (A[0][1] * rl[1]) + (A[0][2] * rl[2]);
252 r[1] = (A[1][0] * rl[0]) + (A[1][1] * rl[1]) + (A[1][2] * rl[2]);
253 r[2] = (A[2][0] * rl[0]) + (A[2][1] * rl[1]) + (A[2][2] * rl[2]);
254
255 }
256
257 void RigidBody::body2Lab( double r[3] ){
258
259 double rb[3]; // the body frame vector
260
261 rb[0] = r[0];
262 rb[1] = r[1];
263 rb[2] = r[2];
264
265 r[0] = (A[0][0] * rb[0]) + (A[1][0] * rb[1]) + (A[2][0] * rb[2]);
266 r[1] = (A[0][1] * rb[0]) + (A[1][1] * rb[1]) + (A[2][1] * rb[2]);
267 r[2] = (A[0][2] * rb[0]) + (A[1][2] * rb[1]) + (A[2][2] * rb[2]);
268
269 }
270
271 void RigidBody::calcRefCoords( ) {
272
273 int i,j,k;
274 double mtmp;
275 vec3 apos;
276 double refCOM[3];
277
278 mass = 0.0;
279 for (j=0; j<3; j++)
280 refCOM[j] = 0.0;
281
282 for (i = 0; i < myAtoms.size(); i++) {
283 mtmp = myAtoms[i]->getMass();
284 mass += mtmp;
285
286 apos = refCoords[i];
287
288 for(j = 0; j < 3; j++) {
289 refCOM[j] += apos[j]*mtmp;
290 }
291 }
292
293 for(j = 0; j < 3; j++)
294 refCOM[j] /= mass;
295
296 for (i = 0; i < myAtoms.size(); i++) {
297 apos = refCoords[i];
298 for (j=0; j < 3; j++) {
299 apos[j] = apos[j] - refCOM[j];
300 }
301 refCoords[i] = apos;
302 }
303
304 }
305
306 void RigidBody::doEulerToRotMat(vec3 &euler, mat3x3 &myA ){
307
308 double phi, theta, psi;
309
310 phi = euler[0];
311 theta = euler[1];
312 psi = euler[2];
313
314 myA[0][0] = (cos(phi) * cos(psi)) - (sin(phi) * cos(theta) * sin(psi));
315 myA[0][1] = (sin(phi) * cos(psi)) + (cos(phi) * cos(theta) * sin(psi));
316 myA[0][2] = sin(theta) * sin(psi);
317
318 myA[1][0] = -(cos(phi) * sin(psi)) - (sin(phi) * cos(theta) * cos(psi));
319 myA[1][1] = -(sin(phi) * sin(psi)) + (cos(phi) * cos(theta) * cos(psi));
320 myA[1][2] = sin(theta) * cos(psi);
321
322 myA[2][0] = sin(phi) * sin(theta);
323 myA[2][1] = -cos(phi) * sin(theta);
324 myA[2][2] = cos(theta);
325
326 }
327
328 void RigidBody::calcForcesAndTorques() {
329
330 // Convert Atomic forces and torques to total forces and torques:
331 int i, j;
332 double apos[3];
333 double afrc[3];
334 double atrq[3];
335 double rpos[3];
336
337 zeroForces();
338
339 for (i = 0; i < myAtoms.size(); i++) {
340
341 myAtoms[i]->getPos(apos);
342 myAtoms[i]->getFrc(afrc);
343
344 for (j=0; j<3; j++) {
345 rpos[j] = apos[j] - pos[j];
346 frc[j] += afrc[j];
347 }
348
349 trq[0] += rpos[1]*afrc[2] - rpos[2]*afrc[1];
350 trq[1] += rpos[2]*afrc[0] - rpos[0]*afrc[2];
351 trq[2] += rpos[0]*afrc[1] - rpos[1]*afrc[0];
352
353 // If the atom has a torque associated with it, then we also need to
354 // migrate the torques onto the center of mass:
355
356 if (myAtoms[i]->isDirectional()) {
357
358 myAtoms[i]->getTrq(atrq);
359
360 for (j=0; j<3; j++)
361 trq[j] += atrq[j];
362 }
363 }
364
365 // Convert Torque to Body-fixed coordinates:
366 // (Actually, on second thought, don't. Integrator does this now.)
367 // lab2Body(trq);
368
369 forces_good = true;
370
371 }
372
373 void RigidBody::updateAtoms() {
374 int i, j;
375 vec3 ref;
376 double apos[3];
377 DirectionalAtom* dAtom;
378
379 for (i = 0; i < myAtoms.size(); i++) {
380
381 ref = refCoords[i];
382
383 body2Lab(ref.vec);
384
385 for (j = 0; j<3; j++)
386 apos[j] = pos[j] + ref.vec[j];
387
388 myAtoms[i]->setPos(apos);
389
390 if (myAtoms[i]->isDirectional()) {
391
392 dAtom = (DirectionalAtom *) myAtoms[i];
393 dAtom->rotateBy( A );
394
395 }
396 }
397 }
398
399 void RigidBody::getGrad( double grad[6] ) {
400
401 double myEuler[3];
402 double phi, theta, psi;
403 double cphi, sphi, ctheta, stheta;
404 double ephi[3];
405 double etheta[3];
406 double epsi[3];
407
408 this->getEulerAngles(myEuler);
409
410 phi = myEuler[0];
411 theta = myEuler[1];
412 psi = myEuler[2];
413
414 cphi = cos(phi);
415 sphi = sin(phi);
416 ctheta = cos(theta);
417 stheta = sin(theta);
418
419 // get unit vectors along the phi, theta and psi rotation axes
420
421 ephi[0] = 0.0;
422 ephi[1] = 0.0;
423 ephi[2] = 1.0;
424
425 etheta[0] = cphi;
426 etheta[1] = sphi;
427 etheta[2] = 0.0;
428
429 epsi[0] = stheta * cphi;
430 epsi[1] = stheta * sphi;
431 epsi[2] = ctheta;
432
433 for (int j = 0 ; j<3; j++)
434 grad[j] = frc[j];
435
436 grad[3] = 0.0;
437 grad[4] = 0.0;
438 grad[5] = 0.0;
439
440 for (int j = 0; j < 3; j++ ) {
441
442 grad[3] += trq[j]*ephi[j];
443 grad[4] += trq[j]*etheta[j];
444 grad[5] += trq[j]*epsi[j];
445
446 }
447
448 }
449
450 /**
451 * getEulerAngles computes a set of Euler angle values consistent
452 * with an input rotation matrix. They are returned in the following
453 * order:
454 * myEuler[0] = phi;
455 * myEuler[1] = theta;
456 * myEuler[2] = psi;
457 */
458 void RigidBody::getEulerAngles(double myEuler[3]) {
459
460 // We use so-called "x-convention", which is the most common
461 // definition. In this convention, the rotation given by Euler
462 // angles (phi, theta, psi), where the first rotation is by an angle
463 // phi about the z-axis, the second is by an angle theta (0 <= theta
464 // <= 180) about the x-axis, and the third is by an angle psi about
465 // the z-axis (again).
466
467
468 double phi,theta,psi,eps;
469 double pi;
470 double cphi,ctheta,cpsi;
471 double sphi,stheta,spsi;
472 double b[3];
473 int flip[3];
474
475 // set the tolerance for Euler angles and rotation elements
476
477 eps = 1.0e-8;
478
479 theta = acos(min(1.0,max(-1.0,A[2][2])));
480 ctheta = A[2][2];
481 stheta = sqrt(1.0 - ctheta * ctheta);
482
483 // when sin(theta) is close to 0, we need to consider the
484 // possibility of a singularity. In this case, we can assign an
485 // arbitary value to phi (or psi), and then determine the psi (or
486 // phi) or vice-versa. We'll assume that phi always gets the
487 // rotation, and psi is 0 in cases of singularity. we use atan2
488 // instead of atan, since atan2 will give us -Pi to Pi. Since 0 <=
489 // theta <= 180, sin(theta) will be always non-negative. Therefore,
490 // it never changes the sign of both of the parameters passed to
491 // atan2.
492
493 if (fabs(stheta) <= eps){
494 psi = 0.0;
495 phi = atan2(-A[1][0], A[0][0]);
496 }
497 // we only have one unique solution
498 else{
499 phi = atan2(A[2][0], -A[2][1]);
500 psi = atan2(A[0][2], A[1][2]);
501 }
502
503 //wrap phi and psi, make sure they are in the range from 0 to 2*Pi
504 //if (phi < 0)
505 // phi += M_PI;
506
507 //if (psi < 0)
508 // psi += M_PI;
509
510 myEuler[0] = phi;
511 myEuler[1] = theta;
512 myEuler[2] = psi;
513
514 return;
515 }
516
517 double RigidBody::max(double x, double y) {
518 return (x > y) ? x : y;
519 }
520
521 double RigidBody::min(double x, double y) {
522 return (x > y) ? y : x;
523 }
524
525 void RigidBody::findCOM() {
526
527 size_t i;
528 int j;
529 double mtmp;
530 double ptmp[3];
531 double vtmp[3];
532
533 for(j = 0; j < 3; j++) {
534 pos[j] = 0.0;
535 vel[j] = 0.0;
536 }
537 mass = 0.0;
538
539 for (i = 0; i < myAtoms.size(); i++) {
540
541 mtmp = myAtoms[i]->getMass();
542 myAtoms[i]->getPos(ptmp);
543 myAtoms[i]->getVel(vtmp);
544
545 mass += mtmp;
546
547 for(j = 0; j < 3; j++) {
548 pos[j] += ptmp[j]*mtmp;
549 vel[j] += vtmp[j]*mtmp;
550 }
551
552 }
553
554 for(j = 0; j < 3; j++) {
555 pos[j] /= mass;
556 vel[j] /= mass;
557 }
558
559 com_good = true;
560 }
561
562 void RigidBody::findOrient() {
563
564 size_t it;
565 int i, j;
566 double ptmp[3];
567 double Itmp[3][3];
568 double evals[3];
569 double evects[3][3];
570 double r2, mtmp, len;
571
572 if (!com_good) findCOM();
573
574 // Calculate inertial tensor matrix elements:
575
576 for (i = 0; i < 3; i++)
577 for (j = 0; j < 3; j++)
578 Itmp[i][j] = 0.0;
579
580 for (it = 0; it < myAtoms.size(); it++) {
581
582 mtmp = myAtoms[it]->getMass();
583 myAtoms[it]->getPos(ptmp);
584
585 for (j = 0; j < 3; j++)
586 ptmp[j] = pos[j] - ptmp[j];
587
588 r2 = norm3(ptmp);
589
590 for (i = 0; i < 3; i++) {
591 for (j = 0; j < 3; j++) {
592
593 if (i==j) Itmp[i][j] = mtmp * r2;
594
595 Itmp[i][j] -= mtmp * ptmp[i]*ptmp[j];
596 }
597 }
598 }
599
600 diagonalize3x3(Itmp, evals, sU);
601
602 // zero out I and then fill the diagonals with the moments of inertia:
603
604 for (i = 0; i < 3; i++) {
605 for (j = 0; j < 3; j++) {
606 I[i][j] = 0.0;
607 }
608 I[i][i] = evals[i];
609 }
610
611 // renormalize column vectors:
612
613 for (i=0; i < 3; i++) {
614 len = 0.0;
615 for (j = 0; j < 3; j++) {
616 len += sU[i][j]*sU[i][j];
617 }
618 len = sqrt(len);
619 for (j = 0; j < 3; j++) {
620 sU[i][j] /= len;
621 }
622 }
623
624 // sU now contains the coordinates of the 'special' frame;
625
626 orient_good = true;
627
628 }