ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE/libmdtools/Integrator.cpp
Revision: 563
Committed: Mon Jun 23 21:24:03 2003 UTC (21 years ago) by mmeineke
File size: 15623 byte(s)
Log Message:
Doing some work to debug the constraint code.

File Contents

# User Rev Content
1 mmeineke 558 #include <iostream>
2     #include <cstdlib>
3 mmeineke 561 #include <cmath>
4 mmeineke 558
5     #ifdef IS_MPI
6     #include "mpiSimulation.hpp"
7     #include <unistd.h>
8     #endif //is_mpi
9    
10     #include "Integrator.hpp"
11     #include "simError.h"
12    
13    
14 mmeineke 561 Integrator::Integrator( SimInfo *theInfo, ForceFields* the_ff ){
15 mmeineke 558
16     info = theInfo;
17     myFF = the_ff;
18     isFirst = 1;
19    
20     molecules = info->molecules;
21     nMols = info->n_mol;
22    
23     // give a little love back to the SimInfo object
24    
25     if( info->the_integrator != NULL ) delete info->the_integrator;
26     info->the_integrator = this;
27    
28     nAtoms = info->n_atoms;
29    
30     // check for constraints
31    
32     constrainedA = NULL;
33     constrainedB = NULL;
34     constrainedDsqr = NULL;
35     moving = NULL;
36     moved = NULL;
37 mmeineke 561 oldPos = NULL;
38 mmeineke 558
39     nConstrained = 0;
40    
41     checkConstraints();
42     }
43    
44     Integrator::~Integrator() {
45    
46     if( nConstrained ){
47     delete[] constrainedA;
48     delete[] constrainedB;
49     delete[] constrainedDsqr;
50     delete[] moving;
51     delete[] moved;
52 mmeineke 561 delete[] oldPos;
53 mmeineke 558 }
54    
55     }
56    
57     void Integrator::checkConstraints( void ){
58    
59    
60     isConstrained = 0;
61    
62     Constraint *temp_con;
63     Constraint *dummy_plug;
64     temp_con = new Constraint[info->n_SRI];
65     nConstrained = 0;
66     int constrained = 0;
67    
68     SRI** theArray;
69     for(int i = 0; i < nMols; i++){
70    
71     theArray = (SRI**) molecules[i].getMyBonds();
72     for(int j=0; j<molecules[i].getNBonds(); j++){
73    
74     constrained = theArray[j]->is_constrained();
75    
76     if(constrained){
77    
78     dummy_plug = theArray[j]->get_constraint();
79     temp_con[nConstrained].set_a( dummy_plug->get_a() );
80     temp_con[nConstrained].set_b( dummy_plug->get_b() );
81     temp_con[nConstrained].set_dsqr( dummy_plug->get_dsqr() );
82    
83     nConstrained++;
84     constrained = 0;
85     }
86     }
87    
88     theArray = (SRI**) molecules[i].getMyBends();
89     for(int j=0; j<molecules[i].getNBends(); j++){
90    
91     constrained = theArray[j]->is_constrained();
92    
93     if(constrained){
94    
95     dummy_plug = theArray[j]->get_constraint();
96     temp_con[nConstrained].set_a( dummy_plug->get_a() );
97     temp_con[nConstrained].set_b( dummy_plug->get_b() );
98     temp_con[nConstrained].set_dsqr( dummy_plug->get_dsqr() );
99    
100     nConstrained++;
101     constrained = 0;
102     }
103     }
104    
105     theArray = (SRI**) molecules[i].getMyTorsions();
106     for(int j=0; j<molecules[i].getNTorsions(); j++){
107    
108     constrained = theArray[j]->is_constrained();
109    
110     if(constrained){
111    
112     dummy_plug = theArray[j]->get_constraint();
113     temp_con[nConstrained].set_a( dummy_plug->get_a() );
114     temp_con[nConstrained].set_b( dummy_plug->get_b() );
115     temp_con[nConstrained].set_dsqr( dummy_plug->get_dsqr() );
116    
117     nConstrained++;
118     constrained = 0;
119     }
120     }
121     }
122    
123     if(nConstrained > 0){
124    
125     isConstrained = 1;
126    
127     if(constrainedA != NULL ) delete[] constrainedA;
128     if(constrainedB != NULL ) delete[] constrainedB;
129     if(constrainedDsqr != NULL ) delete[] constrainedDsqr;
130    
131     constrainedA = new int[nConstrained];
132     constrainedB = new int[nConstrained];
133     constrainedDsqr = new double[nConstrained];
134    
135     for( int i = 0; i < nConstrained; i++){
136    
137     constrainedA[i] = temp_con[i].get_a();
138     constrainedB[i] = temp_con[i].get_b();
139     constrainedDsqr[i] = temp_con[i].get_dsqr();
140 mmeineke 563
141     cerr << "constraint " << constrainedA[i] << " <-> " << constrainedB[i]
142     << " => " << constrainedDsqr[i] << "\n";
143 mmeineke 558 }
144    
145    
146     // save oldAtoms to check for lode balanceing later on.
147    
148     oldAtoms = nAtoms;
149    
150     moving = new int[nAtoms];
151     moved = new int[nAtoms];
152    
153 mmeineke 561 oldPos = new double[nAtoms*3];
154 mmeineke 558 }
155    
156     delete[] temp_con;
157     }
158    
159    
160     void Integrator::integrate( void ){
161    
162     int i, j; // loop counters
163    
164     double runTime = info->run_time;
165     double sampleTime = info->sampleTime;
166     double statusTime = info->statusTime;
167     double thermalTime = info->thermalTime;
168    
169     double currSample;
170     double currThermal;
171     double currStatus;
172     double currTime;
173    
174     int calcPot, calcStress;
175     int isError;
176    
177 mmeineke 561
178    
179 mmeineke 558 tStats = new Thermo( info );
180 mmeineke 561 statOut = new StatWriter( info );
181     dumpOut = new DumpWriter( info );
182 mmeineke 558
183 mmeineke 561 atoms = info->atoms;
184 mmeineke 558 DirectionalAtom* dAtom;
185 mmeineke 561
186     dt = info->dt;
187 mmeineke 558 dt2 = 0.5 * dt;
188    
189     // initialize the forces before the first step
190    
191     myFF->doForces(1,1);
192    
193     if( info->setTemp ){
194    
195     tStats->velocitize();
196     }
197    
198 mmeineke 561 dumpOut->writeDump( 0.0 );
199     statOut->writeStat( 0.0 );
200 mmeineke 558
201     calcPot = 0;
202     calcStress = 0;
203     currSample = sampleTime;
204     currThermal = thermalTime;
205     currStatus = statusTime;
206     currTime = 0.0;;
207    
208 mmeineke 559
209     readyCheck();
210    
211     #ifdef IS_MPI
212     strcpy( checkPointMsg,
213     "The integrator is ready to go." );
214     MPIcheckPoint();
215     #endif // is_mpi
216    
217 mmeineke 561
218     pos = Atom::getPosArray();
219     vel = Atom::getVelArray();
220     frc = Atom::getFrcArray();
221     trq = Atom::getTrqArray();
222     Amat = Atom::getAmatArray();
223    
224 mmeineke 558 while( currTime < runTime ){
225    
226     if( (currTime+dt) >= currStatus ){
227     calcPot = 1;
228     calcStress = 1;
229     }
230 mmeineke 561
231 mmeineke 558 integrateStep( calcPot, calcStress );
232    
233     currTime += dt;
234    
235     if( info->setTemp ){
236     if( currTime >= currThermal ){
237     tStats->velocitize();
238     currThermal += thermalTime;
239     }
240     }
241    
242     if( currTime >= currSample ){
243 mmeineke 561 dumpOut->writeDump( currTime );
244 mmeineke 558 currSample += sampleTime;
245     }
246    
247     if( currTime >= currStatus ){
248 mmeineke 561 statOut->writeStat( currTime );
249 mmeineke 558 calcPot = 0;
250     calcStress = 0;
251     currStatus += statusTime;
252     }
253 mmeineke 559
254     #ifdef IS_MPI
255     strcpy( checkPointMsg,
256     "successfully took a time step." );
257     MPIcheckPoint();
258     #endif // is_mpi
259    
260 mmeineke 558 }
261    
262 mmeineke 561 dumpOut->writeFinal();
263 mmeineke 558
264 mmeineke 561 delete dumpOut;
265     delete statOut;
266 mmeineke 558 }
267    
268     void Integrator::integrateStep( int calcPot, int calcStress ){
269    
270 mmeineke 561
271    
272 mmeineke 558 // Position full step, and velocity half step
273    
274 mmeineke 561 preMove();
275 mmeineke 558 moveA();
276     if( nConstrained ) constrainA();
277    
278     // calc forces
279    
280     myFF->doForces(calcPot,calcStress);
281    
282     // finish the velocity half step
283    
284     moveB();
285     if( nConstrained ) constrainB();
286    
287     }
288    
289    
290     void Integrator::moveA( void ){
291    
292     int i,j,k;
293     int atomIndex, aMatIndex;
294     DirectionalAtom* dAtom;
295     double Tb[3];
296     double ji[3];
297 mmeineke 561 double angle;
298 mmeineke 558
299     for( i=0; i<nAtoms; i++ ){
300     atomIndex = i * 3;
301     aMatIndex = i * 9;
302    
303     // velocity half step
304     for( j=atomIndex; j<(atomIndex+3); j++ )
305     vel[j] += ( dt2 * frc[j] / atoms[i]->getMass() ) * eConvert;
306    
307     // position whole step
308     for( j=atomIndex; j<(atomIndex+3); j++ )
309     pos[j] += dt * vel[j];
310    
311    
312     if( atoms[i]->isDirectional() ){
313    
314     dAtom = (DirectionalAtom *)atoms[i];
315    
316     // get and convert the torque to body frame
317    
318     Tb[0] = dAtom->getTx();
319     Tb[1] = dAtom->getTy();
320     Tb[2] = dAtom->getTz();
321    
322     dAtom->lab2Body( Tb );
323    
324     // get the angular momentum, and propagate a half step
325    
326     ji[0] = dAtom->getJx() + ( dt2 * Tb[0] ) * eConvert;
327     ji[1] = dAtom->getJy() + ( dt2 * Tb[1] ) * eConvert;
328     ji[2] = dAtom->getJz() + ( dt2 * Tb[2] ) * eConvert;
329    
330     // use the angular velocities to propagate the rotation matrix a
331     // full time step
332    
333     // rotate about the x-axis
334     angle = dt2 * ji[0] / dAtom->getIxx();
335 mmeineke 561 this->rotate( 1, 2, angle, ji, &Amat[aMatIndex] );
336 mmeineke 558
337     // rotate about the y-axis
338     angle = dt2 * ji[1] / dAtom->getIyy();
339 mmeineke 561 this->rotate( 2, 0, angle, ji, &Amat[aMatIndex] );
340 mmeineke 558
341     // rotate about the z-axis
342     angle = dt * ji[2] / dAtom->getIzz();
343 mmeineke 561 this->rotate( 0, 1, angle, ji, &Amat[aMatIndex] );
344 mmeineke 558
345     // rotate about the y-axis
346     angle = dt2 * ji[1] / dAtom->getIyy();
347 mmeineke 561 this->rotate( 2, 0, angle, ji, &Amat[aMatIndex] );
348 mmeineke 558
349     // rotate about the x-axis
350     angle = dt2 * ji[0] / dAtom->getIxx();
351 mmeineke 561 this->rotate( 1, 2, angle, ji, &Amat[aMatIndex] );
352 mmeineke 558
353     dAtom->setJx( ji[0] );
354     dAtom->setJy( ji[1] );
355     dAtom->setJz( ji[2] );
356     }
357    
358     }
359     }
360    
361    
362     void Integrator::moveB( void ){
363     int i,j,k;
364     int atomIndex;
365     DirectionalAtom* dAtom;
366     double Tb[3];
367     double ji[3];
368    
369     for( i=0; i<nAtoms; i++ ){
370     atomIndex = i * 3;
371    
372     // velocity half step
373     for( j=atomIndex; j<(atomIndex+3); j++ )
374     vel[j] += ( dt2 * frc[j] / atoms[i]->getMass() ) * eConvert;
375    
376     if( atoms[i]->isDirectional() ){
377    
378     dAtom = (DirectionalAtom *)atoms[i];
379    
380     // get and convert the torque to body frame
381    
382     Tb[0] = dAtom->getTx();
383     Tb[1] = dAtom->getTy();
384     Tb[2] = dAtom->getTz();
385    
386     dAtom->lab2Body( Tb );
387    
388     // get the angular momentum, and complete the angular momentum
389     // half step
390    
391     ji[0] = dAtom->getJx() + ( dt2 * Tb[0] ) * eConvert;
392     ji[1] = dAtom->getJy() + ( dt2 * Tb[1] ) * eConvert;
393     ji[2] = dAtom->getJz() + ( dt2 * Tb[2] ) * eConvert;
394    
395     dAtom->setJx( ji[0] );
396     dAtom->setJy( ji[1] );
397     dAtom->setJz( ji[2] );
398     }
399     }
400    
401     }
402    
403     void Integrator::preMove( void ){
404     int i;
405    
406     if( nConstrained ){
407 mmeineke 561
408 mmeineke 558 for(i=0; i<(nAtoms*3); i++) oldPos[i] = pos[i];
409     }
410     }
411    
412     void Integrator::constrainA(){
413    
414     int i,j,k;
415     int done;
416     double pxab, pyab, pzab;
417     double rxab, ryab, rzab;
418 mmeineke 563 int a, b, ax, ay, az, bx, by, bz;
419 mmeineke 558 double rma, rmb;
420     double dx, dy, dz;
421 mmeineke 561 double rpab;
422 mmeineke 558 double rabsq, pabsq, rpabsq;
423     double diffsq;
424     double gab;
425     int iteration;
426    
427    
428    
429     for( i=0; i<nAtoms; i++){
430    
431     moving[i] = 0;
432     moved[i] = 1;
433     }
434    
435    
436     iteration = 0;
437     done = 0;
438     while( !done && (iteration < maxIteration )){
439    
440     done = 1;
441     for(i=0; i<nConstrained; i++){
442    
443     a = constrainedA[i];
444     b = constrainedB[i];
445 mmeineke 563
446     ax = (a*3) + 0;
447     ay = (a*3) + 1;
448     az = (a*3) + 2;
449    
450     bx = (b*3) + 0;
451     by = (b*3) + 1;
452     bz = (b*3) + 2;
453    
454    
455 mmeineke 558 if( moved[a] || moved[b] ){
456    
457 mmeineke 563 pxab = pos[ax] - pos[bx];
458     pyab = pos[ay] - pos[by];
459     pzab = pos[az] - pos[bz];
460 mmeineke 558
461     //periodic boundary condition
462     pxab = pxab - info->box_x * copysign(1, pxab)
463 mmeineke 563 * (int)( fabs(pxab / info->box_x) + 0.5);
464 mmeineke 558 pyab = pyab - info->box_y * copysign(1, pyab)
465 mmeineke 563 * (int)( fabs(pyab / info->box_y) + 0.5);
466 mmeineke 558 pzab = pzab - info->box_z * copysign(1, pzab)
467 mmeineke 563 * (int)( fabs(pzab / info->box_z) + 0.5);
468 mmeineke 558
469     pabsq = pxab * pxab + pyab * pyab + pzab * pzab;
470 mmeineke 561 rabsq = constrainedDsqr[i];
471 mmeineke 558 diffsq = pabsq - rabsq;
472    
473     // the original rattle code from alan tidesley
474 mmeineke 563 if (fabs(diffsq) > (tol*rabsq*2)) {
475     rxab = oldPos[ax] - oldPos[bx];
476     ryab = oldPos[ay] - oldPos[by];
477     rzab = oldPos[az] - oldPos[bz];
478 mmeineke 558
479     rxab = rxab - info->box_x * copysign(1, rxab)
480 mmeineke 563 * (int)( fabs(rxab / info->box_x) + 0.5);
481 mmeineke 558 ryab = ryab - info->box_y * copysign(1, ryab)
482 mmeineke 563 * (int)( fabs(ryab / info->box_y) + 0.5);
483 mmeineke 558 rzab = rzab - info->box_z * copysign(1, rzab)
484 mmeineke 563 * (int)( fabs(rzab / info->box_z) + 0.5);
485 mmeineke 558
486     rpab = rxab * pxab + ryab * pyab + rzab * pzab;
487     rpabsq = rpab * rpab;
488    
489    
490     if (rpabsq < (rabsq * -diffsq)){
491 mmeineke 563
492     cerr << "rpabsq = " << rpabsq << ", rabsq = " << rabsq
493     << ", -diffsq = " << -diffsq << "\n";
494    
495 mmeineke 558 #ifdef IS_MPI
496     a = atoms[a]->getGlobalIndex();
497     b = atoms[b]->getGlobalIndex();
498     #endif //is_mpi
499     sprintf( painCave.errMsg,
500 mmeineke 563 "Constraint failure in constrainA at atom %d and %d.\n",
501 mmeineke 558 a, b );
502     painCave.isFatal = 1;
503     simError();
504     }
505    
506     rma = 1.0 / atoms[a]->getMass();
507     rmb = 1.0 / atoms[b]->getMass();
508    
509     gab = diffsq / ( 2.0 * ( rma + rmb ) * rpab );
510     dx = rxab * gab;
511     dy = ryab * gab;
512     dz = rzab * gab;
513    
514 mmeineke 563 pos[ax] += rma * dx;
515     pos[ay] += rma * dy;
516     pos[az] += rma * dz;
517 mmeineke 558
518 mmeineke 563 pos[bx] -= rmb * dx;
519     pos[by] -= rmb * dy;
520     pos[bz] -= rmb * dz;
521 mmeineke 558
522     dx = dx / dt;
523     dy = dy / dt;
524     dz = dz / dt;
525    
526 mmeineke 563 vel[ax] += rma * dx;
527     vel[ay] += rma * dy;
528     vel[az] += rma * dz;
529 mmeineke 558
530 mmeineke 563 vel[bx] -= rmb * dx;
531     vel[by] -= rmb * dy;
532     vel[bz] -= rmb * dz;
533 mmeineke 558
534     moving[a] = 1;
535     moving[b] = 1;
536     done = 0;
537     }
538     }
539     }
540    
541     for(i=0; i<nAtoms; i++){
542    
543     moved[i] = moving[i];
544     moving[i] = 0;
545     }
546    
547     iteration++;
548 mmeineke 563 cerr << "iterainA = " << iteration << "\n";
549 mmeineke 558 }
550    
551     if( !done ){
552    
553 mmeineke 561 sprintf( painCave.errMsg,
554 mmeineke 558 "Constraint failure in constrainA, too many iterations: %d\n",
555 mmeineke 561 iteration );
556 mmeineke 558 painCave.isFatal = 1;
557     simError();
558     }
559    
560     }
561    
562     void Integrator::constrainB( void ){
563    
564     int i,j,k;
565     int done;
566     double vxab, vyab, vzab;
567     double rxab, ryab, rzab;
568 mmeineke 563 int a, b, ax, ay, az, bx, by, bz;
569 mmeineke 558 double rma, rmb;
570     double dx, dy, dz;
571     double rabsq, pabsq, rvab;
572     double diffsq;
573     double gab;
574     int iteration;
575    
576 mmeineke 561 for(i=0; i<nAtoms; i++){
577 mmeineke 558 moving[i] = 0;
578     moved[i] = 1;
579     }
580    
581     done = 0;
582 mmeineke 561 iteration = 0;
583 mmeineke 558 while( !done && (iteration < maxIteration ) ){
584    
585     for(i=0; i<nConstrained; i++){
586    
587     a = constrainedA[i];
588     b = constrainedB[i];
589    
590 mmeineke 563 ax = 3*a +0;
591     ay = 3*a +1;
592     az = 3*a +2;
593    
594     bx = 3*b +0;
595     by = 3*b +1;
596     bz = 3*b +2;
597    
598 mmeineke 558 if( moved[a] || moved[b] ){
599    
600 mmeineke 563 vxab = vel[ax] - vel[bx];
601     vyab = vel[ay] - vel[by];
602     vzab = vel[az] - vel[bz];
603 mmeineke 558
604 mmeineke 563 rxab = pos[ax] - pos[bx];
605     ryab = pos[ay] - pos[by];
606     rzab = pos[az] - pos[bz];
607 mmeineke 558
608     rxab = rxab - info->box_x * copysign(1, rxab)
609 mmeineke 563 * (int)( fabs(rxab / info->box_x) + 0.5);
610 mmeineke 558 ryab = ryab - info->box_y * copysign(1, ryab)
611 mmeineke 563 * (int)( fabs(ryab / info->box_y) + 0.5);
612 mmeineke 558 rzab = rzab - info->box_z * copysign(1, rzab)
613 mmeineke 563 * (int)( fabs(rzab / info->box_z) + 0.5);
614 mmeineke 558
615     rma = 1.0 / atoms[a]->getMass();
616     rmb = 1.0 / atoms[b]->getMass();
617    
618     rvab = rxab * vxab + ryab * vyab + rzab * vzab;
619    
620 mmeineke 561 gab = -rvab / ( ( rma + rmb ) * constrainedDsqr[i] );
621 mmeineke 558
622     if (fabs(gab) > tol) {
623    
624     dx = rxab * gab;
625     dy = ryab * gab;
626     dz = rzab * gab;
627    
628 mmeineke 563 vel[ax] += rma * dx;
629     vel[ay] += rma * dy;
630     vel[az] += rma * dz;
631 mmeineke 558
632 mmeineke 563 vel[bx] -= rmb * dx;
633     vel[by] -= rmb * dy;
634     vel[bz] -= rmb * dz;
635 mmeineke 558
636     moving[a] = 1;
637     moving[b] = 1;
638     done = 0;
639     }
640     }
641     }
642    
643     for(i=0; i<nAtoms; i++){
644     moved[i] = moving[i];
645     moving[i] = 0;
646     }
647    
648     iteration++;
649     }
650    
651     if( !done ){
652    
653    
654 mmeineke 561 sprintf( painCave.errMsg,
655 mmeineke 558 "Constraint failure in constrainB, too many iterations: %d\n",
656 mmeineke 561 iteration );
657 mmeineke 558 painCave.isFatal = 1;
658     simError();
659     }
660    
661     }
662    
663    
664    
665    
666    
667    
668    
669     void Integrator::rotate( int axes1, int axes2, double angle, double ji[3],
670 mmeineke 561 double A[9] ){
671 mmeineke 558
672     int i,j,k;
673     double sinAngle;
674     double cosAngle;
675     double angleSqr;
676     double angleSqrOver4;
677     double top, bottom;
678     double rot[3][3];
679     double tempA[3][3];
680     double tempJ[3];
681    
682     // initialize the tempA
683    
684     for(i=0; i<3; i++){
685     for(j=0; j<3; j++){
686 mmeineke 561 tempA[j][i] = A[3*i + j];
687 mmeineke 558 }
688     }
689    
690     // initialize the tempJ
691    
692     for( i=0; i<3; i++) tempJ[i] = ji[i];
693    
694     // initalize rot as a unit matrix
695    
696     rot[0][0] = 1.0;
697     rot[0][1] = 0.0;
698     rot[0][2] = 0.0;
699    
700     rot[1][0] = 0.0;
701     rot[1][1] = 1.0;
702     rot[1][2] = 0.0;
703    
704     rot[2][0] = 0.0;
705     rot[2][1] = 0.0;
706     rot[2][2] = 1.0;
707    
708     // use a small angle aproximation for sin and cosine
709    
710     angleSqr = angle * angle;
711     angleSqrOver4 = angleSqr / 4.0;
712     top = 1.0 - angleSqrOver4;
713     bottom = 1.0 + angleSqrOver4;
714    
715     cosAngle = top / bottom;
716     sinAngle = angle / bottom;
717    
718     rot[axes1][axes1] = cosAngle;
719     rot[axes2][axes2] = cosAngle;
720    
721     rot[axes1][axes2] = sinAngle;
722     rot[axes2][axes1] = -sinAngle;
723    
724     // rotate the momentum acoording to: ji[] = rot[][] * ji[]
725    
726     for(i=0; i<3; i++){
727     ji[i] = 0.0;
728     for(k=0; k<3; k++){
729     ji[i] += rot[i][k] * tempJ[k];
730     }
731     }
732    
733     // rotate the Rotation matrix acording to:
734     // A[][] = A[][] * transpose(rot[][])
735    
736    
737 mmeineke 561 // NOte for as yet unknown reason, we are performing the
738 mmeineke 558 // calculation as:
739     // transpose(A[][]) = transpose(A[][]) * transpose(rot[][])
740    
741     for(i=0; i<3; i++){
742     for(j=0; j<3; j++){
743 mmeineke 561 A[3*j + i] = 0.0;
744 mmeineke 558 for(k=0; k<3; k++){
745 mmeineke 561 A[3*j + i] += tempA[i][k] * rot[j][k];
746 mmeineke 558 }
747     }
748     }
749     }