1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode:nil; -*- */
2 /* vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 */
3 /*
4   MDAnalysis --- http://mdanalysis.googlecode.com
5 
6   Copyright (c) 2006-2014 Naveen Michaud-Agrawal,
7                 Elizabeth J. Denning, Oliver Beckstein,
8                 and contributors (see AUTHORS for the full list)
9   Released under the GNU Public Licence, v2 or any higher version
10 
11   Please cite your use of MDAnalysis in published work:
12 
13       N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and
14       O. Beckstein. MDAnalysis: A Toolkit for the Analysis of
15       Molecular Dynamics Simulations. J. Comput. Chem. 32 (2011), 2319--2327,
16       in press.
17 */
18 
19 #ifndef __DISTANCES_H
20 #define __DISTANCES_H
21 
22 #include <math.h>
23 
24 #include <float.h>
25 typedef float coordinate[3];
26 
27 #ifdef PARALLEL
28   #include <omp.h>
29   #define USED_OPENMP 1
30 #else
31   #define USED_OPENMP 0
32 #endif
33 
minimum_image(double * x,float * box,float * inverse_box)34 static void minimum_image(double *x, float *box, float *inverse_box)
35 {
36   int i;
37   double s;
38   for (i=0; i<3; i++) {
39     if (box[i] > FLT_EPSILON) {
40       s = inverse_box[i] * x[i];
41       x[i] = box[i] * (s - round(s));
42     }
43   }
44 }
45 
minimum_image_triclinic(double * dx,coordinate * box)46 static void minimum_image_triclinic(double *dx, coordinate* box)
47 {
48   // Minimum image convention for triclinic systems, modelled after domain.cpp in LAMMPS
49   // Assumes that there is a maximum separation of 1 box length (enforced in dist functions
50   // by moving all particles to inside the box before calculating separations)
51   // Requires a box
52   // Assumes box having zero values for box[0][1], box[0][2] and box [1][2]
53   double dmin[3], rx[3], ry[3], rz[3];
54   double min = FLT_MAX, d;
55   int i, x, y, z;
56 
57   for (x = -1; x < 2; ++x) {
58     rx[0] = dx[0] + box[0][0] * (float)x;
59     rx[1] = dx[1];
60     rx[2] = dx[2];
61     for (y = -1; y < 2; ++y) {
62       ry[0] = rx[0] + box[1][0] * (float)y;
63       ry[1] = rx[1] + box[1][1] * (float)y;
64       ry[2] = rx[2];
65       for (z = -1; z < 2; ++z) {
66         rz[0] = ry[0] + box[2][0] * (float)z;
67         rz[1] = ry[1] + box[2][1] * (float)z;
68         rz[2] = ry[2] + box[2][2] * (float)z;
69         d = rz[0]*rz[0] + rz[1]*rz[1] + rz[2] * rz[2];
70         if (d < min) {
71           for (i=0; i<3; ++i){
72             min = d;
73             dmin[i] = rz[i];
74           }
75         }
76       }
77     }
78   }
79   for (i =0; i<3; ++i) {
80     dx[i] = dmin[i];
81   }
82 }
83 
_ortho_pbc(coordinate * coords,int numcoords,float * box,float * box_inverse)84 static void _ortho_pbc(coordinate* coords, int numcoords, float* box, float* box_inverse)
85 {
86   int i, s[3];
87   // Moves all coordinates to within the box boundaries for a orthogonal box
88 #ifdef PARALLEL
89 #pragma omp parallel for private(i, s) shared(coords)
90 #endif
91   for (i=0; i < numcoords; i++){
92     s[0] = floor(coords[i][0] * box_inverse[0]);
93     s[1] = floor(coords[i][1] * box_inverse[1]);
94     s[2] = floor(coords[i][2] * box_inverse[2]);
95     coords[i][0] -= s[0] * box[0];
96     coords[i][1] -= s[1] * box[1];
97     coords[i][2] -= s[2] * box[2];
98   }
99 }
100 
_triclinic_pbc(coordinate * coords,int numcoords,coordinate * box,float * box_inverse)101 static void _triclinic_pbc(coordinate* coords, int numcoords, coordinate* box, float* box_inverse)
102 {
103   int i, s;
104   // Inverse of matrix box (here called "m")
105   //   [           1/m00                 ,        0      ,   0  ]
106   //   [        -m10/(m00*m11)           ,      1/m11    ,   0  ]
107   //   [(m10*m21/(m00*m11) - m20/m00)/m22, -m21/(m11*m22), 1/m22]
108   float bi00 = box_inverse[0];
109   float bi11 = box_inverse[1];
110   float bi22 = box_inverse[2];
111   float bi01 = -box[1][0]*bi00*bi11;
112   float bi02 = (box[1][0]*box[2][1]*bi11 - box[2][0])*bi00*bi22;
113   float bi12 = -box[2][1]*bi11*bi22;
114   // Moves all coordinates to within the box boundaries for a triclinic box
115   // Assumes box having zero values for box[0][1], box[0][2] and box [1][2]
116 #ifdef PARALLEL
117 #pragma omp parallel for private(i, s) shared(coords)
118 #endif
119   for (i=0; i < numcoords; i++){
120     // translate coords[i] to central cell along c-axis
121     s = floor(coords[i][2]*bi22);
122     coords[i][2] -= s * box[2][2];
123     coords[i][1] -= s * box[2][1];
124     coords[i][0] -= s * box[2][0];
125     // translate remainder of coords[i] to central cell along b-axis
126     s = floor(coords[i][1]*bi11 + coords[i][2]*bi12);
127     coords[i][1] -= s * box[1][1];
128     coords[i][0] -= s * box[1][0];
129     // translate remainder of coords[i] to central cell along a-axis
130     s = floor(coords[i][0]*bi00 + coords[i][1]*bi01 + coords[i][2]*bi02);
131     coords[i][0] -= s * box[0][0];
132   }
133 }
134 
_calc_distance_array(coordinate * ref,int numref,coordinate * conf,int numconf,double * distances)135 static void _calc_distance_array(coordinate* ref, int numref, coordinate* conf,
136                                  int numconf, double* distances)
137 {
138   int i, j;
139   double dx[3];
140   double rsq;
141 
142 #ifdef PARALLEL
143 #pragma omp parallel for private(i, j, dx, rsq) shared(distances)
144 #endif
145   for (i=0; i<numref; i++) {
146     for (j=0; j<numconf; j++) {
147       dx[0] = conf[j][0] - ref[i][0];
148       dx[1] = conf[j][1] - ref[i][1];
149       dx[2] = conf[j][2] - ref[i][2];
150       rsq = (dx[0]*dx[0]) + (dx[1]*dx[1]) + (dx[2]*dx[2]);
151       *(distances+i*numconf+j) = sqrt(rsq);
152     }
153   }
154 }
155 
_calc_distance_array_ortho(coordinate * ref,int numref,coordinate * conf,int numconf,float * box,double * distances)156 static void _calc_distance_array_ortho(coordinate* ref, int numref, coordinate* conf,
157                                        int numconf, float* box, double* distances)
158 {
159   int i, j;
160   double dx[3];
161   float inverse_box[3];
162   double rsq;
163 
164   inverse_box[0] = 1.0 / box[0];
165   inverse_box[1] = 1.0 / box[1];
166   inverse_box[2] = 1.0 / box[2];
167 #ifdef PARALLEL
168 #pragma omp parallel for private(i, j, dx, rsq) shared(distances)
169 #endif
170   for (i=0; i<numref; i++) {
171     for (j=0; j<numconf; j++) {
172       dx[0] = conf[j][0] - ref[i][0];
173       dx[1] = conf[j][1] - ref[i][1];
174       dx[2] = conf[j][2] - ref[i][2];
175       // Periodic boundaries
176       minimum_image(dx, box, inverse_box);
177       rsq = (dx[0]*dx[0]) + (dx[1]*dx[1]) + (dx[2]*dx[2]);
178       *(distances+i*numconf+j) = sqrt(rsq);
179     }
180   }
181 }
182 
_calc_distance_array_triclinic(coordinate * ref,int numref,coordinate * conf,int numconf,coordinate * box,double * distances)183 static void _calc_distance_array_triclinic(coordinate* ref, int numref,
184                                            coordinate* conf, int numconf,
185                                            coordinate* box, double* distances)
186 {
187   int i, j;
188   double dx[3];
189   float box_inverse[3];
190   double rsq;
191 
192   box_inverse[0] = 1.0 / box[0][0];
193   box_inverse[1] = 1.0 / box[1][1];
194   box_inverse[2] = 1.0 / box[2][2];
195   // Move coords to inside box
196   _triclinic_pbc(ref, numref, box, box_inverse);
197   _triclinic_pbc(conf, numconf, box, box_inverse);
198 
199 #ifdef PARALLEL
200 #pragma omp parallel for private(i, j, dx, rsq) shared(distances)
201 #endif
202   for (i=0; i<numref; i++){
203     for (j=0; j<numconf; j++){
204       dx[0] = conf[j][0] - ref[i][0];
205       dx[1] = conf[j][1] - ref[i][1];
206       dx[2] = conf[j][2] - ref[i][2];
207       minimum_image_triclinic(dx, box);
208       rsq = (dx[0]*dx[0] + dx[1]*dx[1] + dx[2]*dx[2]);
209       *(distances + i*numconf + j) = sqrt(rsq);
210     }
211   }
212 }
213 
_calc_self_distance_array(coordinate * ref,int numref,double * distances,int distnum)214 static void _calc_self_distance_array(coordinate* ref, int numref, double* distances,
215                                       int distnum)
216 {
217   int i, j, distpos;
218   double dx[3];
219   double rsq;
220 
221   distpos = 0;
222 
223 #ifdef PARALLEL
224 #pragma omp parallel for private(i, distpos, j, dx, rsq) shared(distances)
225 #endif
226   for (i=0; i<numref; i++) {
227 #ifdef PARALLEL
228     distpos = i * (2 * numref - i - 1) / 2;  // calculates the offset into distances
229 #endif
230     for (j=i+1; j<numref; j++) {
231       dx[0] = ref[j][0] - ref[i][0];
232       dx[1] = ref[j][1] - ref[i][1];
233       dx[2] = ref[j][2] - ref[i][2];
234       rsq = (dx[0]*dx[0]) + (dx[1]*dx[1]) + (dx[2]*dx[2]);
235       *(distances+distpos) = sqrt(rsq);
236       distpos += 1;
237     }
238   }
239 }
240 
_calc_self_distance_array_ortho(coordinate * ref,int numref,float * box,double * distances,int distnum)241 static void _calc_self_distance_array_ortho(coordinate* ref, int numref, float* box,
242                                             double* distances, int distnum)
243 {
244   int i, j, distpos;
245   double dx[3];
246   float inverse_box[3];
247   double rsq;
248 
249   inverse_box[0] = 1.0 / box[0];
250   inverse_box[1] = 1.0 / box[1];
251   inverse_box[2] = 1.0 / box[2];
252   distpos = 0;
253 
254 #ifdef PARALLEL
255 #pragma omp parallel for private(i, distpos, j, dx, rsq) shared(distances)
256 #endif
257   for (i=0; i<numref; i++) {
258 #ifdef PARALLEL
259     distpos = i * (2 * numref - i - 1) / 2;  // calculates the offset into distances
260 #endif
261     for (j=i+1; j<numref; j++) {
262       dx[0] = ref[j][0] - ref[i][0];
263       dx[1] = ref[j][1] - ref[i][1];
264       dx[2] = ref[j][2] - ref[i][2];
265       // Periodic boundaries
266       minimum_image(dx, box, inverse_box);
267       rsq = (dx[0]*dx[0]) + (dx[1]*dx[1]) + (dx[2]*dx[2]);
268       *(distances+distpos) = sqrt(rsq);
269       distpos += 1;
270     }
271   }
272 }
273 
_calc_self_distance_array_triclinic(coordinate * ref,int numref,coordinate * box,double * distances,int distnum)274 static void _calc_self_distance_array_triclinic(coordinate* ref, int numref,
275                                                 coordinate* box, double *distances,
276                                                 int distnum)
277 {
278   int i, j, distpos;
279   double dx[3];
280   double rsq;
281   float box_inverse[3];
282 
283   box_inverse[0] = 1.0 / box[0][0];
284   box_inverse[1] = 1.0 / box[1][1];
285   box_inverse[2] = 1.0 / box[2][2];
286 
287   _triclinic_pbc(ref, numref, box, box_inverse);
288 
289   distpos = 0;
290 
291 #ifdef PARALLEL
292 #pragma omp parallel for private(i, distpos, j, dx, rsq) shared(distances)
293 #endif
294   for (i=0; i<numref; i++){
295 #ifdef PARALLEL
296     distpos = i * (2 * numref - i - 1) / 2;  // calculates the offset into distances
297 #endif
298     for (j=i+1; j<numref; j++){
299       dx[0] = ref[j][0] - ref[i][0];
300       dx[1] = ref[j][1] - ref[i][1];
301       dx[2] = ref[j][2] - ref[i][2];
302       minimum_image_triclinic(dx, box);
303       rsq = (dx[0]*dx[0] + dx[1]*dx[1] + dx[2]*dx[2]);
304       *(distances + distpos) = sqrt(rsq);
305       distpos += 1;
306     }
307   }
308 }
309 
_coord_transform(float coords[][3],int numCoords,float box[][3])310 void _coord_transform(float coords[][3], int numCoords, float box[][3])
311 {
312   int i, j, k;
313   float newpos[3];
314   // Matrix multiplication inCoords * box = outCoords
315   // Multiplication done in place using temp array 'new'
316   // Used to transform coordinates to/from S/R space in trilinic boxes
317 #ifdef PARALLEL
318 #pragma omp parallel for private(i, j, k, newpos) shared(coords)
319 #endif
320   for (i=0; i < numCoords; i++){
321     newpos[0] = 0.0;
322     newpos[1] = 0.0;
323     newpos[2] = 0.0;
324     for (j=0; j<3; j++){
325       for (k=0; k<3; k++){
326         newpos[j] += coords[i][k] * box[k][j];
327       }
328     }
329     coords[i][0] = newpos[0];
330     coords[i][1] = newpos[1];
331     coords[i][2] = newpos[2];
332   }
333 }
334 
_calc_bond_distance(coordinate * atom1,coordinate * atom2,int numatom,double * distances)335 static void _calc_bond_distance(coordinate* atom1, coordinate* atom2,
336                                 int numatom, double* distances)
337 {
338   int i;
339   double dx[3];
340   double rsq;
341 
342 #ifdef PARALLEL
343 #pragma omp parallel for private(i, dx, rsq) shared(distances)
344 #endif
345   for (i=0; i<numatom; i++) {
346     dx[0] = atom1[i][0] - atom2[i][0];
347     dx[1] = atom1[i][1] - atom2[i][1];
348     dx[2] = atom1[i][2] - atom2[i][2];
349     rsq = (dx[0]*dx[0])+(dx[1]*dx[1])+(dx[2]*dx[2]);
350     *(distances+i) = sqrt(rsq);
351   }
352 }
353 
_calc_bond_distance_ortho(coordinate * atom1,coordinate * atom2,int numatom,float * box,double * distances)354 static void _calc_bond_distance_ortho(coordinate* atom1, coordinate* atom2,
355                                       int numatom, float* box, double* distances)
356 {
357   int i;
358   double dx[3];
359   float inverse_box[3];
360   double rsq;
361 
362   inverse_box[0] = 1.0/box[0];
363   inverse_box[1] = 1.0/box[1];
364   inverse_box[2] = 1.0/box[2];
365 
366 #ifdef PARALLEL
367 #pragma omp parallel for private(i, dx, rsq) shared(distances)
368 #endif
369   for (i=0; i<numatom; i++) {
370     dx[0] = atom1[i][0] - atom2[i][0];
371     dx[1] = atom1[i][1] - atom2[i][1];
372     dx[2] = atom1[i][2] - atom2[i][2];
373     // PBC time!
374     minimum_image(dx, box, inverse_box);
375     rsq = (dx[0]*dx[0])+(dx[1]*dx[1])+(dx[2]*dx[2]);
376     *(distances+i) = sqrt(rsq);
377   }
378 }
_calc_bond_distance_triclinic(coordinate * atom1,coordinate * atom2,int numatom,coordinate * box,double * distances)379 static void _calc_bond_distance_triclinic(coordinate* atom1, coordinate* atom2,
380                                           int numatom, coordinate* box,
381                                           double* distances)
382 {
383   int i;
384   double dx[3];
385   float box_inverse[3];
386   double rsq;
387 
388   box_inverse[0] = 1.0/box[0][0];
389   box_inverse[1] = 1.0/box[1][1];
390   box_inverse[2] = 1.0/box[2][2];
391 
392   _triclinic_pbc(atom1, numatom, box, box_inverse);
393   _triclinic_pbc(atom2, numatom, box, box_inverse);
394 
395 #ifdef PARALLEL
396 #pragma omp parallel for private(i, dx, rsq) shared(distances)
397 #endif
398   for (i=0; i<numatom; i++) {
399     dx[0] = atom1[i][0] - atom2[i][0];
400     dx[1] = atom1[i][1] - atom2[i][1];
401     dx[2] = atom1[i][2] - atom2[i][2];
402     // PBC time!
403     minimum_image_triclinic(dx, box);
404     rsq = (dx[0]*dx[0])+(dx[1]*dx[1])+(dx[2]*dx[2]);
405     *(distances+i) = sqrt(rsq);
406   }
407 }
408 
_calc_angle(coordinate * atom1,coordinate * atom2,coordinate * atom3,int numatom,double * angles)409 static void _calc_angle(coordinate* atom1, coordinate* atom2,
410                         coordinate* atom3, int numatom, double* angles)
411 {
412   int i;
413   double rji[3], rjk[3];
414   double x, y, xp[3];
415 
416 #ifdef PARALLEL
417 #pragma omp parallel for private(i, rji, rjk, x, xp, y) shared(angles)
418 #endif
419   for (i=0; i<numatom; i++) {
420     rji[0] = atom1[i][0] - atom2[i][0];
421     rji[1] = atom1[i][1] - atom2[i][1];
422     rji[2] = atom1[i][2] - atom2[i][2];
423 
424     rjk[0] = atom3[i][0] - atom2[i][0];
425     rjk[1] = atom3[i][1] - atom2[i][1];
426     rjk[2] = atom3[i][2] - atom2[i][2];
427 
428     x = rji[0]*rjk[0] + rji[1]*rjk[1] + rji[2]*rjk[2];
429 
430     xp[0] = rji[1]*rjk[2] - rji[2]*rjk[1];
431     xp[1] =-rji[0]*rjk[2] + rji[2]*rjk[0];
432     xp[2] = rji[0]*rjk[1] - rji[1]*rjk[0];
433 
434     y = sqrt(xp[0]*xp[0] + xp[1]*xp[1] + xp[2]*xp[2]);
435 
436     *(angles+i) = atan2(y,x);
437   }
438 }
439 
_calc_angle_ortho(coordinate * atom1,coordinate * atom2,coordinate * atom3,int numatom,float * box,double * angles)440 static void _calc_angle_ortho(coordinate* atom1, coordinate* atom2,
441                               coordinate* atom3, int numatom,
442                               float* box, double* angles)
443 {
444   // Angle is calculated between two vectors
445   // pbc option ensures that vectors are constructed between atoms in the same image as eachother
446   // ie that vectors don't go across a boxlength
447   // it doesn't matter if vectors are from different boxes however
448   int i;
449   double rji[3], rjk[3];
450   double x, y, xp[3];
451   float inverse_box[3];
452 
453   inverse_box[0] = 1.0/box[0];
454   inverse_box[1] = 1.0/box[1];
455   inverse_box[2] = 1.0/box[2];
456 
457 #ifdef PARALLEL
458 #pragma omp parallel for private(i, rji, rjk, x, xp, y) shared(angles)
459 #endif
460   for (i=0; i<numatom; i++) {
461     rji[0] = atom1[i][0] - atom2[i][0];
462     rji[1] = atom1[i][1] - atom2[i][1];
463     rji[2] = atom1[i][2] - atom2[i][2];
464     minimum_image(rji, box, inverse_box);
465 
466     rjk[0] = atom3[i][0] - atom2[i][0];
467     rjk[1] = atom3[i][1] - atom2[i][1];
468     rjk[2] = atom3[i][2] - atom2[i][2];
469     minimum_image(rjk, box, inverse_box);
470 
471     x = rji[0]*rjk[0] + rji[1]*rjk[1] + rji[2]*rjk[2];
472 
473     xp[0] = rji[1]*rjk[2] - rji[2]*rjk[1];
474     xp[1] =-rji[0]*rjk[2] + rji[2]*rjk[0];
475     xp[2] = rji[0]*rjk[1] - rji[1]*rjk[0];
476 
477     y = sqrt(xp[0]*xp[0] + xp[1]*xp[1] + xp[2]*xp[2]);
478 
479     *(angles+i) = atan2(y,x);
480   }
481 }
482 
_calc_angle_triclinic(coordinate * atom1,coordinate * atom2,coordinate * atom3,int numatom,coordinate * box,double * angles)483 static void _calc_angle_triclinic(coordinate* atom1, coordinate* atom2,
484                                   coordinate* atom3, int numatom,
485                                   coordinate* box, double* angles)
486 {
487   // Triclinic version of min image aware angle calculate, see above
488   int i;
489   double rji[3], rjk[3];
490   double x, y, xp[3];
491   float box_inverse[3];
492 
493   box_inverse[0] = 1.0/box[0][0];
494   box_inverse[1] = 1.0/box[1][1];
495   box_inverse[2] = 1.0/box[2][2];
496 
497   _triclinic_pbc(atom1, numatom, box, box_inverse);
498   _triclinic_pbc(atom2, numatom, box, box_inverse);
499   _triclinic_pbc(atom3, numatom, box, box_inverse);
500 
501 #ifdef PARALLEL
502 #pragma omp parallel for private(i, rji, rjk, x, xp, y) shared(angles)
503 #endif
504   for (i=0; i<numatom; i++) {
505     rji[0] = atom1[i][0] - atom2[i][0];
506     rji[1] = atom1[i][1] - atom2[i][1];
507     rji[2] = atom1[i][2] - atom2[i][2];
508     minimum_image_triclinic(rji, box);
509 
510     rjk[0] = atom3[i][0] - atom2[i][0];
511     rjk[1] = atom3[i][1] - atom2[i][1];
512     rjk[2] = atom3[i][2] - atom2[i][2];
513     minimum_image_triclinic(rjk, box);
514 
515     x = rji[0]*rjk[0] + rji[1]*rjk[1] + rji[2]*rjk[2];
516 
517     xp[0] = rji[1]*rjk[2] - rji[2]*rjk[1];
518     xp[1] =-rji[0]*rjk[2] + rji[2]*rjk[0];
519     xp[2] = rji[0]*rjk[1] - rji[1]*rjk[0];
520 
521     y = sqrt(xp[0]*xp[0] + xp[1]*xp[1] + xp[2]*xp[2]);
522 
523     *(angles+i) = atan2(y,x);
524   }
525 }
526 
_calc_dihedral_angle(double * va,double * vb,double * vc,double * result)527 static void _calc_dihedral_angle(double* va, double* vb, double* vc, double* result)
528 {
529   // Returns atan2 from vectors va, vb, vc
530   double n1[3], n2[3];
531   double xp[3], vb_norm;
532   double x, y;
533 
534   //n1 is normal vector to -va, vb
535   //n2 is normal vector to -vb, vc
536   n1[0] =-va[1]*vb[2] + va[2]*vb[1];
537   n1[1] = va[0]*vb[2] - va[2]*vb[0];
538   n1[2] =-va[0]*vb[1] + va[1]*vb[0];
539 
540   n2[0] =-vb[1]*vc[2] + vb[2]*vc[1];
541   n2[1] = vb[0]*vc[2] - vb[2]*vc[0];
542   n2[2] =-vb[0]*vc[1] + vb[1]*vc[0];
543 
544   // x = dot(n1,n2) = cos theta
545   x = (n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2]);
546 
547   // xp = cross(n1,n2)
548   xp[0] = n1[1]*n2[2] - n1[2]*n2[1];
549   xp[1] =-n1[0]*n2[2] + n1[2]*n2[0];
550   xp[2] = n1[0]*n2[1] - n1[1]*n2[0];
551 
552   vb_norm = sqrt(vb[0]*vb[0] + vb[1]*vb[1] + vb[2]*vb[2]);
553 
554   y = (xp[0]*vb[0] + xp[1]*vb[1] + xp[2]*vb[2]) / vb_norm;
555 
556   if ( (fabs(x) == 0.0) && (fabs(y) == 0.0) ) // numpy consistency
557   {
558     *result = NAN;
559     return;
560   }
561 
562   *result = atan2(y, x); //atan2 is better conditioned than acos
563 }
564 
_calc_dihedral(coordinate * atom1,coordinate * atom2,coordinate * atom3,coordinate * atom4,int numatom,double * angles)565 static void _calc_dihedral(coordinate* atom1, coordinate* atom2,
566                            coordinate* atom3, coordinate* atom4,
567                            int numatom, double* angles)
568 {
569   int i;
570   double va[3], vb[3], vc[3];
571 
572 #ifdef PARALLEL
573 #pragma omp parallel for private(i, va, vb, vc) shared(angles)
574 #endif
575   for (i=0; i<numatom; i++) {
576     // connecting vectors between all 4 atoms: 1 -va-> 2 -vb-> 3 -vc-> 4
577     va[0] = atom2[i][0] - atom1[i][0];
578     va[1] = atom2[i][1] - atom1[i][1];
579     va[2] = atom2[i][2] - atom1[i][2];
580 
581     vb[0] = atom3[i][0] - atom2[i][0];
582     vb[1] = atom3[i][1] - atom2[i][1];
583     vb[2] = atom3[i][2] - atom2[i][2];
584 
585     vc[0] = atom4[i][0] - atom3[i][0];
586     vc[1] = atom4[i][1] - atom3[i][1];
587     vc[2] = atom4[i][2] - atom3[i][2];
588 
589     _calc_dihedral_angle(va, vb, vc, angles + i);
590   }
591 }
592 
_calc_dihedral_ortho(coordinate * atom1,coordinate * atom2,coordinate * atom3,coordinate * atom4,int numatom,float * box,double * angles)593 static void _calc_dihedral_ortho(coordinate* atom1, coordinate* atom2,
594                                  coordinate* atom3, coordinate* atom4,
595                                  int numatom, float* box, double* angles)
596 {
597   int i;
598   double va[3], vb[3], vc[3];
599   float inverse_box[3];
600 
601   inverse_box[0] = 1.0/box[0];
602   inverse_box[1] = 1.0/box[1];
603   inverse_box[2] = 1.0/box[2];
604 
605 #ifdef PARALLEL
606 #pragma omp parallel for private(i, va, vb, vc) shared(angles)
607 #endif
608   for (i=0; i<numatom; i++) {
609     // connecting vectors between all 4 atoms: 1 -va-> 2 -vb-> 3 -vc-> 4
610     va[0] = atom2[i][0] - atom1[i][0];
611     va[1] = atom2[i][1] - atom1[i][1];
612     va[2] = atom2[i][2] - atom1[i][2];
613     minimum_image(va, box, inverse_box);
614 
615     vb[0] = atom3[i][0] - atom2[i][0];
616     vb[1] = atom3[i][1] - atom2[i][1];
617     vb[2] = atom3[i][2] - atom2[i][2];
618     minimum_image(vb, box, inverse_box);
619 
620     vc[0] = atom4[i][0] - atom3[i][0];
621     vc[1] = atom4[i][1] - atom3[i][1];
622     vc[2] = atom4[i][2] - atom3[i][2];
623     minimum_image(vc, box, inverse_box);
624 
625     _calc_dihedral_angle(va, vb, vc, angles + i);
626   }
627 }
628 
_calc_dihedral_triclinic(coordinate * atom1,coordinate * atom2,coordinate * atom3,coordinate * atom4,int numatom,coordinate * box,double * angles)629 static void _calc_dihedral_triclinic(coordinate* atom1, coordinate* atom2,
630                                      coordinate* atom3, coordinate* atom4,
631                                      int numatom, coordinate* box, double* angles)
632 {
633   int i;
634   double va[3], vb[3], vc[3];
635   float box_inverse[3];
636 
637   box_inverse[0] = 1.0/box[0][0];
638   box_inverse[1] = 1.0/box[1][1];
639   box_inverse[2] = 1.0/box[2][2];
640 
641   _triclinic_pbc(atom1, numatom, box, box_inverse);
642   _triclinic_pbc(atom2, numatom, box, box_inverse);
643   _triclinic_pbc(atom3, numatom, box, box_inverse);
644   _triclinic_pbc(atom4, numatom, box, box_inverse);
645 
646 #ifdef PARALLEL
647 #pragma omp parallel for private(i, va, vb, vc) shared(angles)
648 #endif
649   for (i=0; i<numatom; i++) {
650     // connecting vectors between all 4 atoms: 1 -va-> 2 -vb-> 3 -vc-> 4
651     va[0] = atom2[i][0] - atom1[i][0];
652     va[1] = atom2[i][1] - atom1[i][1];
653     va[2] = atom2[i][2] - atom1[i][2];
654     minimum_image_triclinic(va, box);
655 
656     vb[0] = atom3[i][0] - atom2[i][0];
657     vb[1] = atom3[i][1] - atom2[i][1];
658     vb[2] = atom3[i][2] - atom2[i][2];
659     minimum_image_triclinic(vb, box);
660 
661     vc[0] = atom4[i][0] - atom3[i][0];
662     vc[1] = atom4[i][1] - atom3[i][1];
663     vc[2] = atom4[i][2] - atom3[i][2];
664     minimum_image_triclinic(vc, box);
665 
666     _calc_dihedral_angle(va, vb, vc, angles + i);
667   }
668 }
669 #endif
670