ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/branches/new_design/OOPSE-3.0/src/primitives/RigidBody.cpp
Revision: 1490
Committed: Fri Sep 24 04:16:43 2004 UTC (19 years, 9 months ago) by gezelter
Original Path: trunk/OOPSE-3.0/src/primitives/RigidBody.cpp
File size: 15928 byte(s)
Log Message:
Import of OOPSE v. 2.0

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