ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/SHAPES/RigidBody.cpp
Revision: 1283
Committed: Mon Jun 21 15:54:27 2004 UTC (20 years ago) by gezelter
File size: 13068 byte(s)
Log Message:
a few bug fixes

File Contents

# Content
1 #include <math.h>
2 #include <iostream>
3 #include "RigidBody.hpp"
4 #include "VDWAtom.hpp"
5 #include "MatVec3.h"
6
7 RigidBody::RigidBody() {
8 is_linear = false;
9 linear_axis = -1;
10 momIntTol = 1e-6;
11 }
12
13 RigidBody::~RigidBody() {
14 }
15
16 void RigidBody::addAtom(VDWAtom* at) {
17
18 vec3 coords;
19
20 myAtoms.push_back(at);
21
22 at->getPos(coords.vec);
23 refCoords.push_back(coords);
24 }
25
26 void RigidBody::getPos(double theP[3]){
27 for (int i = 0; i < 3 ; i++)
28 theP[i] = pos[i];
29 }
30
31 void RigidBody::setPos(double theP[3]){
32 for (int i = 0; i < 3 ; i++)
33 pos[i] = theP[i];
34 }
35
36
37 void RigidBody::setEuler( double phi, double theta, double psi ){
38
39 A[0][0] = (cos(phi) * cos(psi)) - (sin(phi) * cos(theta) * sin(psi));
40 A[0][1] = (sin(phi) * cos(psi)) + (cos(phi) * cos(theta) * sin(psi));
41 A[0][2] = sin(theta) * sin(psi);
42
43 A[1][0] = -(cos(phi) * sin(psi)) - (sin(phi) * cos(theta) * cos(psi));
44 A[1][1] = -(sin(phi) * sin(psi)) + (cos(phi) * cos(theta) * cos(psi));
45 A[1][2] = sin(theta) * cos(psi);
46
47 A[2][0] = sin(phi) * sin(theta);
48 A[2][1] = -cos(phi) * sin(theta);
49 A[2][2] = cos(theta);
50
51 printf("A[2][x] = %lf\t%lf\t%lf\n", A[2][0], A[2][1], A[2][2]);
52
53 }
54
55 void RigidBody::getQ( double q[4] ){
56
57 double t, s;
58 double ad1, ad2, ad3;
59
60 t = A[0][0] + A[1][1] + A[2][2] + 1.0;
61 if( t > 0.0 ){
62
63 s = 0.5 / sqrt( t );
64 q[0] = 0.25 / s;
65 q[1] = (A[1][2] - A[2][1]) * s;
66 q[2] = (A[2][0] - A[0][2]) * s;
67 q[3] = (A[0][1] - A[1][0]) * s;
68 }
69 else{
70
71 ad1 = fabs( A[0][0] );
72 ad2 = fabs( A[1][1] );
73 ad3 = fabs( A[2][2] );
74
75 if( ad1 >= ad2 && ad1 >= ad3 ){
76
77 s = 2.0 * sqrt( 1.0 + A[0][0] - A[1][1] - A[2][2] );
78 q[0] = (A[1][2] + A[2][1]) / s;
79 q[1] = 0.5 / s;
80 q[2] = (A[0][1] + A[1][0]) / s;
81 q[3] = (A[0][2] + A[2][0]) / s;
82 }
83 else if( ad2 >= ad1 && ad2 >= ad3 ){
84
85 s = sqrt( 1.0 + A[1][1] - A[0][0] - A[2][2] ) * 2.0;
86 q[0] = (A[0][2] + A[2][0]) / s;
87 q[1] = (A[0][1] + A[1][0]) / s;
88 q[2] = 0.5 / s;
89 q[3] = (A[1][2] + A[2][1]) / s;
90 }
91 else{
92
93 s = sqrt( 1.0 + A[2][2] - A[0][0] - A[1][1] ) * 2.0;
94 q[0] = (A[0][1] + A[1][0]) / s;
95 q[1] = (A[0][2] + A[2][0]) / s;
96 q[2] = (A[1][2] + A[2][1]) / s;
97 q[3] = 0.5 / s;
98 }
99 }
100 }
101
102 void RigidBody::setQ( double the_q[4] ){
103
104 double q0Sqr, q1Sqr, q2Sqr, q3Sqr;
105
106 q0Sqr = the_q[0] * the_q[0];
107 q1Sqr = the_q[1] * the_q[1];
108 q2Sqr = the_q[2] * the_q[2];
109 q3Sqr = the_q[3] * the_q[3];
110
111 A[0][0] = q0Sqr + q1Sqr - q2Sqr - q3Sqr;
112 A[0][1] = 2.0 * ( the_q[1] * the_q[2] + the_q[0] * the_q[3] );
113 A[0][2] = 2.0 * ( the_q[1] * the_q[3] - the_q[0] * the_q[2] );
114
115 A[1][0] = 2.0 * ( the_q[1] * the_q[2] - the_q[0] * the_q[3] );
116 A[1][1] = q0Sqr - q1Sqr + q2Sqr - q3Sqr;
117 A[1][2] = 2.0 * ( the_q[2] * the_q[3] + the_q[0] * the_q[1] );
118
119 A[2][0] = 2.0 * ( the_q[1] * the_q[3] + the_q[0] * the_q[2] );
120 A[2][1] = 2.0 * ( the_q[2] * the_q[3] - the_q[0] * the_q[1] );
121 A[2][2] = q0Sqr - q1Sqr -q2Sqr +q3Sqr;
122
123 }
124
125 void RigidBody::getA( double the_A[3][3] ){
126
127 for (int i = 0; i < 3; i++)
128 for (int j = 0; j < 3; j++)
129 the_A[i][j] = A[i][j];
130
131 }
132
133 void RigidBody::setA( double the_A[3][3] ){
134
135 for (int i = 0; i < 3; i++)
136 for (int j = 0; j < 3; j++)
137 A[i][j] = the_A[i][j];
138
139 }
140
141 void RigidBody::getI( double the_I[3][3] ){
142
143 for (int i = 0; i < 3; i++)
144 for (int j = 0; j < 3; j++)
145 the_I[i][j] = I[i][j];
146
147 }
148
149 void RigidBody::lab2Body( double r[3] ){
150
151 double rl[3]; // the lab frame vector
152
153 rl[0] = r[0];
154 rl[1] = r[1];
155 rl[2] = r[2];
156
157 r[0] = (A[0][0] * rl[0]) + (A[0][1] * rl[1]) + (A[0][2] * rl[2]);
158 r[1] = (A[1][0] * rl[0]) + (A[1][1] * rl[1]) + (A[1][2] * rl[2]);
159 r[2] = (A[2][0] * rl[0]) + (A[2][1] * rl[1]) + (A[2][2] * rl[2]);
160
161 }
162
163 void RigidBody::body2Lab( double r[3] ){
164
165 double rb[3]; // the body frame vector
166
167 rb[0] = r[0];
168 rb[1] = r[1];
169 rb[2] = r[2];
170
171 r[0] = (A[0][0] * rb[0]) + (A[1][0] * rb[1]) + (A[2][0] * rb[2]);
172 r[1] = (A[0][1] * rb[0]) + (A[1][1] * rb[1]) + (A[2][1] * rb[2]);
173 r[2] = (A[0][2] * rb[0]) + (A[1][2] * rb[1]) + (A[2][2] * rb[2]);
174
175 }
176
177 void RigidBody::calcRefCoords( ) {
178
179 int i, j, it, n_linear_coords, pAxis, maxAxis, midAxis;
180 double mtmp;
181 vec3 apos;
182 double refCOM[3];
183 vec3 ptmp;
184 double Itmp[3][3];
185 double pAxisMat[3][3], pAxisRotMat[3][3];
186 double evals[3];
187 double r, r2, len;
188 double iMat[3][3];
189 double test[3];
190
191 // First, find the center of mass:
192
193 mass = 0.0;
194 for (j=0; j<3; j++)
195 refCOM[j] = 0.0;
196
197 for (i = 0; i < myAtoms.size(); i++) {
198 mtmp = myAtoms[i]->getMass();
199 mass += mtmp;
200
201 apos = refCoords[i];
202 for(j = 0; j < 3; j++) {
203 refCOM[j] += apos[j]*mtmp;
204 }
205 }
206
207 for(j = 0; j < 3; j++)
208 refCOM[j] /= mass;
209
210 // Next, move the origin of the reference coordinate system to the COM:
211
212 for (i = 0; i < myAtoms.size(); i++) {
213 apos = refCoords[i];
214 for (j=0; j < 3; j++) {
215 apos[j] = apos[j] - refCOM[j];
216 }
217 refCoords[i] = apos;
218 }
219
220 // Moment of Inertia calculation
221
222 for (i = 0; i < 3; i++)
223 for (j = 0; j < 3; j++)
224 Itmp[i][j] = 0.0;
225
226 for (it = 0; it < myAtoms.size(); it++) {
227
228 mtmp = myAtoms[it]->getMass();
229 ptmp = refCoords[it];
230 r= norm3(ptmp.vec);
231 r2 = r*r;
232
233 for (i = 0; i < 3; i++) {
234 for (j = 0; j < 3; j++) {
235
236 if (i==j) Itmp[i][j] += mtmp * r2;
237
238 Itmp[i][j] -= mtmp * ptmp.vec[i]*ptmp.vec[j];
239 }
240 }
241 }
242
243 diagonalize3x3(Itmp, evals, sU);
244
245 // zero out I and then fill the diagonals with the moments of inertia:
246
247 n_linear_coords = 0;
248
249 for (i = 0; i < 3; i++) {
250 for (j = 0; j < 3; j++) {
251 I[i][j] = 0.0;
252 }
253 I[i][i] = evals[i];
254
255 if (fabs(evals[i]) < momIntTol) {
256 is_linear = true;
257 n_linear_coords++;
258 linear_axis = i;
259 }
260 }
261
262 if (n_linear_coords > 1) {
263 printf(
264 "RigidBody error.\n"
265 "\tSHAPES found more than one axis in this rigid body with a vanishing \n"
266 "\tmoment of inertia. This can happen in one of three ways:\n"
267 "\t 1) Only one atom was specified, or \n"
268 "\t 2) All atoms were specified at the same location, or\n"
269 "\t 3) The programmers did something stupid.\n"
270 "\tIt is silly to use a rigid body to describe this situation. Be smarter.\n"
271 );
272 exit(-1);
273 }
274
275 // renormalize column vectors:
276
277 for (i=0; i < 3; i++) {
278 len = 0.0;
279 for (j = 0; j < 3; j++) {
280 len += sU[i][j]*sU[i][j];
281 }
282 len = sqrt(len);
283 for (j = 0; j < 3; j++) {
284 sU[i][j] /= len;
285 }
286 }
287
288 //sort and reorder the moment axes
289
290 // The only problem below is for molecules like C60 with 3 nearly identical
291 // non-zero moments of inertia. In this case it doesn't really matter which is
292 // the principal axis, so they get assigned nearly randomly depending on the
293 // floating point comparison between eigenvalues
294 if (! is_linear) {
295 pAxis = 0;
296 maxAxis = 0;
297
298 for (i = 0; i < 3; i++) {
299 if (evals[i] < evals[pAxis]) pAxis = i;
300 if (evals[i] > evals[maxAxis]) maxAxis = i;
301 }
302
303 midAxis = 0;
304 for (i=0; i < 3; i++) {
305 if (pAxis != i && maxAxis != i) midAxis = i;
306 }
307 } else {
308 pAxis = linear_axis;
309 // linear molecules have one zero moment of inertia and two identical
310 // moments of inertia. In this case, it doesn't matter which is chosen
311 // as mid and which is max, so just permute from the pAxis:
312 midAxis = (pAxis + 1)%3;
313 maxAxis = (pAxis + 2)%3;
314 }
315
316 //let z be the smallest and x be the largest eigenvalue axes
317 for (i=0; i<3; i++){
318 pAxisMat[i][2] = sU[i][pAxis];
319 pAxisMat[i][1] = sU[i][midAxis];
320 pAxisMat[i][0] = sU[i][maxAxis];
321 }
322
323 //calculate the proper rotation matrix
324 transposeMat3(pAxisMat, pAxisRotMat);
325
326
327 for (i=0; i<myAtoms.size(); i++){
328 apos = refCoords[i];
329 printf("%f\t%f\t%f\n",apos[0],apos[1],apos[2]);
330 }
331
332 //rotate the rigid body to the principle axis frame
333 for (i = 0; i < myAtoms.size(); i++) {
334 matVecMul3(pAxisRotMat, refCoords[i].vec, refCoords[i].vec);
335 myAtoms[i]->setPos(refCoords[i].vec);
336 }
337
338 for (i=0; i<myAtoms.size(); i++){
339 apos = refCoords[i];
340 printf("%f\t%f\t%f\n",apos[0],apos[1],apos[2]);
341 }
342
343 identityMat3(iMat);
344 setA(iMat);
345 }
346
347 void RigidBody::doEulerToRotMat(double euler[3], double myA[3][3] ){
348
349 double phi, theta, psi;
350
351 phi = euler[0];
352 theta = euler[1];
353 psi = euler[2];
354
355 myA[0][0] = (cos(phi) * cos(psi)) - (sin(phi) * cos(theta) * sin(psi));
356 myA[0][1] = (sin(phi) * cos(psi)) + (cos(phi) * cos(theta) * sin(psi));
357 myA[0][2] = sin(theta) * sin(psi);
358
359 myA[1][0] = -(cos(phi) * sin(psi)) - (sin(phi) * cos(theta) * cos(psi));
360 myA[1][1] = -(sin(phi) * sin(psi)) + (cos(phi) * cos(theta) * cos(psi));
361 myA[1][2] = sin(theta) * cos(psi);
362
363 myA[2][0] = sin(phi) * sin(theta);
364 myA[2][1] = -cos(phi) * sin(theta);
365 myA[2][2] = cos(theta);
366
367 }
368
369 void RigidBody::updateAtoms() {
370 int i, j;
371 vec3 ref;
372 double apos[3];
373
374 for (i = 0; i < myAtoms.size(); i++) {
375
376 ref = refCoords[i];
377
378 body2Lab(ref.vec);
379
380 for (j = 0; j<3; j++)
381 apos[j] = pos[j] + ref.vec[j];
382
383 myAtoms[i]->setPos(apos);
384
385 }
386 }
387
388 /**
389 * getEulerAngles computes a set of Euler angle values consistent
390 * with an input rotation matrix. They are returned in the following
391 * order:
392 * myEuler[0] = phi;
393 * myEuler[1] = theta;
394 * myEuler[2] = psi;
395 */
396 void RigidBody::getEulerAngles(double myEuler[3]) {
397
398 // We use so-called "x-convention", which is the most common
399 // definition. In this convention, the rotation given by Euler
400 // angles (phi, theta, psi), where the first rotation is by an angle
401 // phi about the z-axis, the second is by an angle theta (0 <= theta
402 // <= 180) about the x-axis, and the third is by an angle psi about
403 // the z-axis (again).
404
405
406 double phi,theta,psi,eps;
407 double ctheta;
408 double stheta;
409
410 // set the tolerance for Euler angles and rotation elements
411
412 eps = 1.0e-8;
413
414 theta = acos(min(1.0,max(-1.0,A[2][2])));
415 ctheta = A[2][2];
416 stheta = sqrt(1.0 - ctheta * ctheta);
417
418 // when sin(theta) is close to 0, we need to consider the
419 // possibility of a singularity. In this case, we can assign an
420 // arbitary value to phi (or psi), and then determine the psi (or
421 // phi) or vice-versa. We'll assume that phi always gets the
422 // rotation, and psi is 0 in cases of singularity. we use atan2
423 // instead of atan, since atan2 will give us -Pi to Pi. Since 0 <=
424 // theta <= 180, sin(theta) will be always non-negative. Therefore,
425 // it never changes the sign of both of the parameters passed to
426 // atan2.
427
428 if (fabs(stheta) <= eps){
429 psi = 0.0;
430 phi = atan2(-A[1][0], A[0][0]);
431 }
432 // we only have one unique solution
433 else{
434 phi = atan2(A[2][0], -A[2][1]);
435 psi = atan2(A[0][2], A[1][2]);
436 }
437
438 //wrap phi and psi, make sure they are in the range from 0 to 2*Pi
439 //if (phi < 0)
440 // phi += M_PI;
441
442 //if (psi < 0)
443 // psi += M_PI;
444
445 myEuler[0] = phi;
446 myEuler[1] = theta;
447 myEuler[2] = psi;
448
449 return;
450 }
451
452 double RigidBody::max(double x, double y) {
453 return (x > y) ? x : y;
454 }
455
456 double RigidBody::min(double x, double y) {
457 return (x > y) ? y : x;
458 }
459
460 double RigidBody::findMaxExtent(){
461 int i;
462 double refAtomPos[3];
463 double maxExtent;
464 double tempExtent;
465
466 //zero the extent variables
467 maxExtent = 0.0;
468 tempExtent = 0.0;
469 for (i=0; i<3; i++)
470 refAtomPos[i] = 0.0;
471
472 //loop over all atoms
473 for (i=0; i<myAtoms.size(); i++){
474 getAtomRefCoor(refAtomPos, i);
475 tempExtent = sqrt(refAtomPos[0]*refAtomPos[0] + refAtomPos[1]*refAtomPos[1]
476 + refAtomPos[2]*refAtomPos[2]);
477 if (tempExtent > maxExtent)
478 maxExtent = tempExtent;
479 }
480 return maxExtent;
481 }
482
483 void RigidBody::findCOM() {
484
485 size_t i;
486 int j;
487 double mtmp;
488 double ptmp[3];
489
490 for(j = 0; j < 3; j++) {
491 pos[j] = 0.0;
492 }
493 mass = 0.0;
494
495 for (i = 0; i < myAtoms.size(); i++) {
496
497 mtmp = myAtoms[i]->getMass();
498 myAtoms[i]->getPos(ptmp);
499
500 mass += mtmp;
501
502 for(j = 0; j < 3; j++) {
503 pos[j] += ptmp[j]*mtmp;
504 }
505
506 }
507
508 for(j = 0; j < 3; j++) {
509 pos[j] /= mass;
510 }
511
512 }
513
514 void RigidBody::getAtomPos(double theP[3], int index){
515 vec3 ref;
516
517 if (index >= myAtoms.size())
518 printf( "%d is an invalid index, current rigid body contains "
519 "%d atoms\n", index, myAtoms.size());
520
521 ref = refCoords[index];
522 body2Lab(ref.vec);
523
524 theP[0] = pos[0] + ref[0];
525 theP[1] = pos[1] + ref[1];
526 theP[2] = pos[2] + ref[2];
527 }
528
529
530 void RigidBody::getAtomRefCoor(double pos[3], int index){
531 vec3 ref;
532
533 ref = refCoords[index];
534 pos[0] = ref[0];
535 pos[1] = ref[1];
536 pos[2] = ref[2];
537
538 }
539
540 double RigidBody::getAtomRpar(int index){
541
542 return myAtoms[index]->getRpar();
543
544 }
545
546 double RigidBody::getAtomEps(int index){
547
548 return myAtoms[index]->getEps();
549
550 }