ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/SHAPES/RigidBody.cpp
Revision: 1282
Committed: Mon Jun 21 15:10:29 2004 UTC (20 years ago) by chrisfen
File size: 12999 byte(s)
Log Message:
Fixed the grid builder rotation problems

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