ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE/libmdtools/RigidBody.cpp
Revision: 1113
Committed: Thu Apr 15 16:18:26 2004 UTC (20 years, 2 months ago) by tim
File size: 13296 byte(s)
Log Message:
fix whole bunch of bugs :-)

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