Enforced rotation: fixed torque calculation for FLEX potential when using mass-weighting
[alexxy/gromacs.git] / src / mdlib / pull_rotation.c
1 /*
2  * 
3  *                This source code is part of
4  * 
5  *                 G   R   O   M   A   C   S
6  * 
7  *          GROningen MAchine for Chemical Simulations
8  * 
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2008, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13  
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  * 
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  * 
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  * 
29  * For more info, check our website at http://www.gromacs.org
30  * 
31  * And Hey:
32  * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
33  */
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include "domdec.h"
42 #include "gmx_wallcycle.h"
43 #include "trnio.h"
44 #include "smalloc.h"
45 #include "network.h"
46 #include "pbc.h"
47 #include "futil.h"
48 #include "mdrun.h"
49 #include "txtdump.h"
50 #include "names.h"
51 #include "mtop_util.h"
52 #include "names.h"
53 #include "nrjac.h"
54 #include "vec.h"
55 #include "gmx_ga2la.h"
56 #include "xvgr.h"
57 #include "gmxfio.h"
58 #include "mpelogging.h"
59 #include "groupcoord.h"
60 #include "pull_rotation.h"
61 #include "gmx_sort.h"
62
63
64 static char *RotStr = {"Enforced rotation:"};
65
66
67 /* Set the minimum weight for the determination of the slab centers */
68 #define WEIGHT_MIN (10*GMX_FLOAT_MIN)
69
70 /* Helper structure for sorting positions along rotation vector             */
71 typedef struct {
72     real xcproj;            /* Projection of xc on the rotation vector        */
73     int ind;                /* Index of xc                                    */
74     real m;                 /* Mass                                           */
75     rvec x;                 /* Position                                       */
76     rvec x_ref;             /* Reference position                             */
77 } sort_along_vec_t;
78
79
80 /* Enforced rotation / flexible: determine the angle of each slab             */
81 typedef struct gmx_slabdata
82 {
83     int  nat;               /* Number of atoms belonging to this slab         */
84     rvec *x;                /* The positions belonging to this slab. In 
85                                general, this should be all positions of the 
86                                whole rotation group, but we leave those away 
87                                that have a small enough weight                */
88     rvec *ref;              /* Same for reference                             */
89     real *weight;           /* The weight for each atom                       */
90 } t_gmx_slabdata;
91
92
93 /* Enforced rotation data for all groups                                      */
94 typedef struct gmx_enfrot
95 {
96     FILE  *out_rot;         /* Output file for rotation data                  */
97     FILE  *out_torque;      /* Output file for torque data                    */
98     FILE  *out_angles;      /* Output file for slab angles for flexible type  */
99     FILE  *out_slabs;       /* Output file for slab centers                   */
100     int   bufsize;          /* Allocation size of buf                         */
101     rvec  *xbuf;            /* Coordinate buffer variable for sorting         */
102     real  *mbuf;            /* Masses buffer variable for sorting             */
103     sort_along_vec_t *data; /* Buffer variable needed for position sorting    */
104     real  *mpi_inbuf;       /* MPI buffer                                     */
105     real  *mpi_outbuf;      /* MPI buffer                                     */
106     int   mpi_bufsize;      /* Allocation size of in & outbuf                 */
107     real  Vrot;             /* (Local) part of the enf. rotation potential    */
108 } t_gmx_enfrot;
109
110
111 /* Global enforced rotation data for a single rotation group                  */
112 typedef struct gmx_enfrotgrp
113 {
114     real    degangle;       /* Rotation angle in degrees                      */
115     matrix  rotmat;         /* Rotation matrix                                */
116     atom_id *ind_loc;       /* Local rotation indices                         */
117     int     nat_loc;        /* Number of local group atoms                    */
118     int     nalloc_loc;     /* Allocation size for ind_loc and weight_loc     */
119
120     real  V;                /* Rotation potential for this rotation group     */
121     rvec  *f_rot_loc;       /* Array to store the forces on the local atoms
122                                resulting from enforced rotation potential     */
123
124     /* Collective coordinates for the whole rotation group */
125     real  *xc_ref_length;   /* Length of each x_rotref vector after x_rotref 
126                                has been put into origin                       */
127     int   *xc_ref_ind;      /* Position of each local atom in the collective
128                                array                                          */
129     rvec  xc_center;        /* Center of the rotation group positions, may
130                                be mass weighted                               */
131     rvec  xc_ref_center;    /* dito, for the reference positions              */
132     rvec  *xc;              /* Current (collective) positions                 */
133     ivec  *xc_shifts;       /* Current (collective) shifts                    */
134     ivec  *xc_eshifts;      /* Extra shifts since last DD step                */
135     rvec  *xc_old;          /* Old (collective) positions                     */
136     rvec  *xc_norm;         /* Normalized form of the current positions       */
137     rvec  *xc_ref_sorted;   /* Reference positions (sorted in the same order 
138                                as xc when sorted)                             */
139     int   *xc_sortind;      /* Where is a position found after sorting?       */
140     real  *mc;              /* Collective masses                              */
141     real  *mc_sorted;
142     real  invmass;          /* one over the total mass of the rotation group  */
143
144     real  torque_v;         /* Torque in the direction of rotation vector     */
145     real  angle_v;          /* Actual angle of the whole rotation group       */
146     /* Fixed rotation only */
147     real  weight_v;         /* Weights for angle determination                */
148     rvec  *xr_loc;          /* Local reference coords, correctly rotated      */
149     rvec  *x_loc_pbc;       /* Local current coords, correct PBC image        */
150     real  *m_loc;           /* Masses of the current local atoms              */
151
152     /* Flexible rotation only */
153     int   nslabs_alloc;     /* For this many slabs memory is allocated        */
154     int   slab_first;       /* Lowermost slab for that the calculation needs 
155                                to be performed at a given time step           */
156     int   slab_last;        /* Uppermost slab ...                             */
157     int   slab_first_ref;   /* First slab for which reference COG is stored   */
158     int   slab_last_ref;    /* Last ...                                       */
159     int   slab_buffer;      /* Slab buffer region around reference slabs      */
160     int   *firstatom;       /* First relevant atom for a slab                 */
161     int   *lastatom;        /* Last relevant atom for a slab                  */
162     rvec  *slab_center;     /* Gaussian-weighted slab center (COG)            */
163     rvec  *slab_center_ref; /* Gaussian-weighted slab COG for the 
164                                reference positions                            */
165     real  *slab_weights;    /* Sum of gaussian weights in a slab              */
166     real  *slab_torque_v;   /* Torque T = r x f for each slab.                */
167                             /* torque_v = m.v = angular momentum in the 
168                                direction of v                                 */
169     real  max_beta;         /* min_gaussian from inputrec->rotgrp is the
170                                minimum value the gaussian must have so that 
171                                the force is actually evaluated max_beta is 
172                                just another way to put it                     */
173     real  *gn_atom;         /* Precalculated gaussians for a single atom      */
174     int   *gn_slabind;      /* Tells to which slab each precalculated gaussian 
175                                belongs                                        */
176     rvec  *slab_innersumvec;/* Inner sum of the flexible2 potential per slab;
177                                this is precalculated for optimization reasons */
178     t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights 
179                                of atoms belonging to a slab                   */
180 } t_gmx_enfrotgrp;
181
182
183 /* Activate output of forces for correctness checks */
184 /* #define PRINT_FORCES */
185 #ifdef PRINT_FORCES
186 #define PRINT_FORCE_J  fprintf(stderr,"f%d = %15.8f %15.8f %15.8f\n",erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
187 #define PRINT_POT_TAU  if (MASTER(cr)) { \
188                            fprintf(stderr,"potential = %15.8f\n" "torque    = %15.8f", erg->V, erg->torque_v); \
189                        }
190 #else
191 #define PRINT_FORCE_J
192 #define PRINT_POT_TAU
193 #endif
194
195 /* Shortcuts for often used queries */
196 #define ISFLEX(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) )
197 #define ISCOLL(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) || (rg->eType==erotgRMPF) || (rg->eType==erotgRM2PF) )
198
199
200
201 static double** allocate_square_matrix(int dim)
202 {
203     int i;
204     double** mat = NULL; 
205     
206     
207     snew(mat, dim);
208     for(i=0; i<dim; i++)
209         snew(mat[i], dim);
210
211     return mat;
212 }
213
214
215 static void free_square_matrix(double** mat, int dim)
216 {
217     int i;
218     
219     
220     for (i=0; i<dim; i++)
221         sfree(mat[i]);
222     sfree(mat);
223 }
224
225
226 /* Output rotation energy, torques, etc. for each rotation group */
227 static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
228 {
229     int      g,i,islab,nslabs=0;
230     int      count;      /* MPI element counter                               */
231     t_rotgrp *rotg;
232     gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
233     gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
234     gmx_bool bFlex;
235
236     
237     er=rot->enfrot;
238     
239     /* Fill the MPI buffer with stuff to reduce: */
240     if (PAR(cr))
241     {
242         count=0;
243         for (g=0; g < rot->ngrp; g++)
244         {
245             rotg = &rot->grp[g];
246             erg = rotg->enfrotgrp;
247             nslabs = erg->slab_last - erg->slab_first + 1;
248             er->mpi_inbuf[count++] = erg->V;
249             er->mpi_inbuf[count++] = erg->torque_v;
250             er->mpi_inbuf[count++] = erg->angle_v;
251             er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
252             if (ISFLEX(rotg))
253             {
254                 /* (Re-)allocate memory for MPI buffer: */
255                 if (er->mpi_bufsize < count+nslabs)
256                 {
257                     er->mpi_bufsize = count+nslabs;
258                     srenew(er->mpi_inbuf , er->mpi_bufsize);
259                     srenew(er->mpi_outbuf, er->mpi_bufsize);
260                 }
261                 for (i=0; i<nslabs; i++)
262                     er->mpi_inbuf[count++] = erg->slab_torque_v[i];
263             }
264         }
265 #ifdef GMX_MPI
266         MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
267 #endif
268         /* Copy back the reduced data from the buffer on the master */
269         if (MASTER(cr))
270         {
271             count=0;
272             for (g=0; g < rot->ngrp; g++)
273             {
274                 rotg = &rot->grp[g];
275                 erg = rotg->enfrotgrp;
276                 nslabs = erg->slab_last - erg->slab_first + 1;
277                 erg->V        = er->mpi_outbuf[count++];
278                 erg->torque_v = er->mpi_outbuf[count++];
279                 erg->angle_v  = er->mpi_outbuf[count++];
280                 erg->weight_v = er->mpi_outbuf[count++];
281                 if (ISFLEX(rotg))
282                 {
283                     for (i=0; i<nslabs; i++)
284                         erg->slab_torque_v[i] = er->mpi_outbuf[count++];
285                 }
286             }
287         }
288     }
289     
290     /* Output */
291     if (MASTER(cr))
292     {
293         /* Angle and torque for each rotation group */
294         for (g=0; g < rot->ngrp; g++)
295         {
296             rotg=&rot->grp[g];
297             bFlex = ISFLEX(rotg);
298
299             erg=rotg->enfrotgrp;
300             
301             /* Output to main rotation output file: */
302             if ( do_per_step(step, rot->nstrout) )
303             {
304                 if (bFlex)
305                     fprintf(er->out_rot, "%12.4f", erg->angle_v); /* RMSD fit angle */
306                 else
307                     fprintf(er->out_rot, "%12.4f", (erg->angle_v/erg->weight_v)*180.0*M_1_PI);
308                 fprintf(er->out_rot, "%12.3e", erg->torque_v);
309                 fprintf(er->out_rot, "%12.3e", erg->V);
310             }
311
312             /* Output to torque log file: */
313             if ( bFlex && do_per_step(step, rot->nstsout) )
314             {
315                 fprintf(er->out_torque, "%12.3e%6d", t, g);
316                 for (i=erg->slab_first; i<=erg->slab_last; i++)
317                 {
318                     islab = i - erg->slab_first;  /* slab index */
319                     /* Only output if enough weight is in slab */
320                     if (erg->slab_weights[islab] > rotg->min_gaussian)
321                         fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
322                 }
323                 fprintf(er->out_torque , "\n");
324             }
325         }
326         if ( do_per_step(step, rot->nstrout) )
327             fprintf(er->out_rot, "\n");
328     }
329 }
330
331
332 /* Add the forces from enforced rotation potential to the local forces.
333  * Should be called after the SR forces have been evaluated */
334 extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
335 {
336     int g,l,ii;
337     t_rotgrp *rotg;
338     gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
339     gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
340
341     
342     er=rot->enfrot;
343     
344     GMX_MPE_LOG(ev_add_rot_forces_start);
345     
346     /* Reduce energy,torque, angles etc. to get the sum values (per rotation group) 
347      * on the master and output these values to file. */
348     if ( do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout) )
349         reduce_output(cr, rot, t, step);
350
351     /* Total rotation potential is the sum over all rotation groups */
352     er->Vrot = 0.0; 
353         
354     /* Loop over enforced rotation groups (usually 1, though)
355      * Apply the forces from rotation potentials */
356     for (g=0; g<rot->ngrp; g++)
357     {
358         rotg = &rot->grp[g];
359         erg=rotg->enfrotgrp;
360         er->Vrot += erg->V;
361         for (l=0; l<erg->nat_loc; l++)
362         {
363             /* Get the right index of the local force */
364             ii = erg->ind_loc[l];
365             /* Add */
366             rvec_inc(f[ii],erg->f_rot_loc[l]);
367         }
368     }
369     
370     PRINT_POT_TAU
371
372     GMX_MPE_LOG(ev_add_rot_forces_finish);
373
374     return (MASTER(cr)? er->Vrot : 0.0);
375 }
376
377
378 /* The Gaussian norm is chosen such that the sum of the gaussian functions
379  * over the slabs is approximately 1.0 everywhere */
380 #define GAUSS_NORM   0.569917543430618
381
382
383 /* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
384  * also does some checks
385  */
386 static double calc_beta_max(real min_gaussian, real slab_dist)
387 {
388     double sigma;
389     double arg;
390     
391     
392     /* Actually the next two checks are already made in grompp */
393     if (slab_dist <= 0)
394         gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
395     if (min_gaussian <= 0)
396         gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
397
398     /* Define the sigma value */
399     sigma = 0.7*slab_dist;
400
401     /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
402     arg = min_gaussian/GAUSS_NORM;
403     if (arg > 1.0)
404         gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
405     
406     return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
407 }
408
409
410 static inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
411 {
412     return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
413 }
414
415
416 static inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
417 {
418     const real norm = GAUSS_NORM;
419     real       sigma;
420
421     
422     /* Define the sigma value */
423     sigma = 0.7*rotg->slab_dist;
424     /* Calculate the Gaussian value of slab n for position curr_x */
425     return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
426 }
427
428
429 /* Returns the weight in a single slab, also calculates the Gaussian- and mass-
430  * weighted sum of positions for that slab */
431 static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
432 {
433     rvec curr_x;              /* The position of an atom                      */
434     rvec curr_x_weighted;     /* The gaussian-weighted position               */
435     real gaussian;            /* A single gaussian weight                     */
436     real wgauss;              /* gaussian times current mass                  */
437     real slabweight = 0.0;    /* The sum of weights in the slab               */
438     int i,islab;
439     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data      */
440
441     
442     erg=rotg->enfrotgrp;
443     clear_rvec(*x_weighted_sum);
444     
445     /* Slab index */
446     islab = j - erg->slab_first;
447     
448     /* Loop over all atoms in the rotation group */
449      for (i=0; i<rotg->nat; i++)
450      {
451          copy_rvec(xc[i], curr_x);
452          gaussian = gaussian_weight(curr_x, rotg, j);
453          wgauss = gaussian * mc[i];
454          svmul(wgauss, curr_x, curr_x_weighted);
455          rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
456          slabweight += wgauss;
457      } /* END of loop over rotation group atoms */
458
459      return slabweight;
460 }
461
462
463 static void get_slab_centers(
464         t_rotgrp *rotg,       /* The rotation group information               */
465         rvec      *xc,        /* The rotation group positions; will 
466                                  typically be enfrotgrp->xc, but at first call 
467                                  it is enfrotgrp->xc_ref                      */
468         real      *mc,        /* The masses of the rotation group atoms       */
469         t_commrec *cr,        /* Communication record                         */
470         int       g,          /* The number of the rotation group             */
471         real      time,       /* Used for output only                         */
472         FILE      *out_slabs, /* For outputting center per slab information   */
473         gmx_bool  bOutStep,   /* Is this an output step?                      */
474         gmx_bool  bReference) /* If this routine is called from
475                                  init_rot_group we need to store
476                                  the reference slab centers                   */
477 {
478     int j,islab;
479     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
480     
481     
482     erg=rotg->enfrotgrp;
483
484     /* Loop over slabs */
485     for (j = erg->slab_first; j <= erg->slab_last; j++)
486     {
487         islab = j - erg->slab_first;
488         erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
489         
490         /* We can do the calculations ONLY if there is weight in the slab! */
491         if (erg->slab_weights[islab] > WEIGHT_MIN)
492         {
493             svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
494         }
495         else
496         {
497             /* We need to check this here, since we divide through slab_weights
498              * in the flexible low-level routines! */
499             gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
500         }
501         
502         /* At first time step: save the centers of the reference structure */
503         if (bReference)
504             copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
505     } /* END of loop over slabs */
506     
507     /* Output on the master */
508     if (MASTER(cr) && bOutStep)
509     {
510         fprintf(out_slabs, "%12.3e%6d", time, g);
511         for (j = erg->slab_first; j <= erg->slab_last; j++)
512         {
513             islab = j - erg->slab_first;
514             fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
515                     j,erg->slab_center[islab][XX],erg->slab_center[islab][YY],erg->slab_center[islab][ZZ]);
516         }
517         fprintf(out_slabs, "\n");
518     }
519 }
520
521
522 static void calc_rotmat(
523         rvec vec,
524         real degangle,  /* Angle alpha of rotation at time t in degrees       */
525         matrix rotmat)  /* Rotation matrix                                    */
526 {
527     real radangle;            /* Rotation angle in radians */
528     real cosa;                /* cosine alpha              */
529     real sina;                /* sine alpha                */
530     real OMcosa;              /* 1 - cos(alpha)            */
531     real dumxy, dumxz, dumyz; /* save computations         */
532     rvec rot_vec;             /* Rotate around rot_vec ... */
533
534
535     radangle = degangle * M_PI/180.0;
536     copy_rvec(vec , rot_vec );
537
538     /* Precompute some variables: */
539     cosa   = cos(radangle);
540     sina   = sin(radangle);
541     OMcosa = 1.0 - cosa;
542     dumxy  = rot_vec[XX]*rot_vec[YY]*OMcosa;
543     dumxz  = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
544     dumyz  = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
545
546     /* Construct the rotation matrix for this rotation group: */
547     /* 1st column: */
548     rotmat[XX][XX] = cosa  + rot_vec[XX]*rot_vec[XX]*OMcosa;
549     rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
550     rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
551     /* 2nd column: */
552     rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
553     rotmat[YY][YY] = cosa  + rot_vec[YY]*rot_vec[YY]*OMcosa;
554     rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
555     /* 3rd column: */
556     rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
557     rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
558     rotmat[ZZ][ZZ] = cosa  + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
559
560 #ifdef PRINTMATRIX
561     int iii,jjj;
562
563     for (iii=0; iii<3; iii++) {
564         for (jjj=0; jjj<3; jjj++)
565             fprintf(stderr, " %10.8f ",  rotmat[iii][jjj]);
566         fprintf(stderr, "\n");
567     }
568 #endif
569 }
570
571
572 /* Calculates torque on the rotation axis tau = position x force */
573 static inline real torque(
574         rvec rotvec,  /* rotation vector; MUST be normalized!                 */
575         rvec force,   /* force                                                */
576         rvec x,       /* position of atom on which the force acts             */
577         rvec pivot)   /* pivot point of rotation axis                         */
578 {
579     rvec vectmp, tau;
580
581     
582     /* Subtract offset */
583     rvec_sub(x,pivot,vectmp);
584     
585     /* position x force */
586     cprod(vectmp, force, tau);
587     
588     /* Return the part of the torque which is parallel to the rotation vector */
589     return iprod(tau, rotvec);
590 }
591
592
593 /* Right-aligned output of value with standard width */
594 static void print_aligned(FILE *fp, char *str)
595 {
596     fprintf(fp, "%12s", str);
597 }
598
599
600 /* Right-aligned output of value with standard short width */
601 static void print_aligned_short(FILE *fp, char *str)
602 {
603     fprintf(fp, "%6s", str);
604 }
605
606
607 static FILE *open_output_file(const char *fn, int steps, const char what[])
608 {
609     FILE *fp;
610     
611     
612     fp = ffopen(fn, "w");
613
614     fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
615             what,steps, steps>1 ? "s":"");
616     
617     return fp;
618 }
619
620
621 /* Open output file for slab center data. Call on master only */
622 static FILE *open_slab_out(t_rot *rot, const char *fn)
623 {
624     FILE      *fp=NULL;
625     int       g,i;
626     t_rotgrp  *rotg;
627
628
629     for (g=0; g<rot->ngrp; g++)
630     {
631         rotg = &rot->grp[g];
632         if (ISFLEX(rotg))
633         {
634             if (NULL == fp)
635                 fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
636             fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
637                     g, erotg_names[rotg->eType], rotg->slab_dist, rotg->bMassW? "centers of mass":"geometrical centers");
638         }
639     }
640     
641     if (fp != NULL)
642     {
643         fprintf(fp, "# Reference centers are listed first (t=-1).\n");
644         fprintf(fp, "# The following columns have the syntax:\n");
645         fprintf(fp, "#     ");
646         print_aligned_short(fp, "t");
647         print_aligned_short(fp, "grp");
648         /* Print ascii legend for the first two entries only ... */
649         for (i=0; i<2; i++)
650         {
651             print_aligned_short(fp, "slab");
652             print_aligned(fp, "X center");
653             print_aligned(fp, "Y center");
654             print_aligned(fp, "Z center");
655         }
656         fprintf(fp, " ...\n");
657         fflush(fp);
658     }
659     
660     return fp;
661 }
662
663
664 /* Open output file and print some general information about the rotation groups.
665  * Call on master only */
666 static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv, 
667                           unsigned long Flags)
668 {
669     FILE       *fp;
670     int        g,nsets;
671     t_rotgrp   *rotg;
672     const char **setname;
673     char       buf[50], buf2[75];
674     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
675     gmx_bool   bFlex;
676
677
678     if (Flags & MD_APPENDFILES)
679     {
680         fp = gmx_fio_fopen(fn,"a");
681     }
682     else
683     {
684         fp = xvgropen(fn, "Rotation angles and energy", "Time [ps]", "angles [degrees] and energies [kJ/mol]", oenv);
685         fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s":"");
686         fprintf(fp, "# The scalar tau is the torque [kJ/mol] in the direction of the rotation vector v.\n");
687         fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
688         fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
689         fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
690         fprintf(fp, "#\n");
691         
692         for (g=0; g<rot->ngrp; g++)
693         {
694             rotg = &rot->grp[g];
695             erg=rotg->enfrotgrp;
696             bFlex = ISFLEX(rotg);
697
698             fprintf(fp, "# Rotation group %d, potential type '%s':\n"      , g, erotg_names[rotg->eType]);
699             fprintf(fp, "# rot_massw%d          %s\n"                      , g, yesno_names[rotg->bMassW]);
700             fprintf(fp, "# rot_vec%d            %12.5e %12.5e %12.5e\n"    , g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
701             fprintf(fp, "# rot_rate%d           %12.5e degrees/ps\n"       , g, rotg->rate);
702             fprintf(fp, "# rot_k%d              %12.5e kJ/(mol*nm^2)\n"    , g, rotg->k);
703             if ( rotg->eType==erotgISO || rotg->eType==erotgPM || rotg->eType==erotgRM || rotg->eType==erotgRM2)
704                 fprintf(fp, "# rot_pivot%d          %12.5e %12.5e %12.5e  nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
705
706             if (bFlex)
707             {
708                 fprintf(fp, "# rot_slab_distance%d   %f nm\n", g, rotg->slab_dist);
709                 fprintf(fp, "# rot_min_gaussian%d   %12.5e\n", g, rotg->min_gaussian);
710             }
711
712             /* Output the centers of the rotation groups for the pivot-free potentials */
713             if ((rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF
714                 || (rotg->eType==erotgFLEXT) || (rotg->eType==erotgFLEX2T)) )
715             {
716                 fprintf(fp, "# %s of ref. grp. %d  %12.5e %12.5e %12.5e\n",
717                         rotg->bMassW? "COM":"COG", g,
718                                 erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
719
720                 fprintf(fp, "# initial %s grp. %d  %12.5e %12.5e %12.5e\n",
721                         rotg->bMassW? "COM":"COG", g,
722                                 erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
723             }
724
725             if ( (rotg->eType == erotgRM2) || (rotg->eType==erotgFLEX2) || (rotg->eType==erotgFLEX2T) )
726             {
727                 fprintf(fp, "# rot_eps%d            %12.5e nm^2\n", g, rotg->eps);
728             }
729         }
730         
731         fprintf(fp, "#\n# Legend for the following data columns:\n");
732         fprintf(fp, "#     ");
733         print_aligned_short(fp, "t");
734         nsets = 0;
735         snew(setname, 4*rot->ngrp);
736         
737         for (g=0; g<rot->ngrp; g++)
738         {
739             rotg = &rot->grp[g];
740             sprintf(buf, "theta_ref%d", g);
741             print_aligned(fp, buf);
742             sprintf(buf2, "%s [degrees]", buf);
743             setname[nsets] = strdup(buf2);
744             nsets++;
745         }
746         for (g=0; g<rot->ngrp; g++)
747         {
748             rotg = &rot->grp[g];
749             bFlex = ISFLEX(rotg);
750
751             /* For flexible axis rotation we use RMSD fitting to determine the
752              * actual angle of the rotation group */
753             if (bFlex)
754                 sprintf(buf, "theta-fit%d", g);
755             else
756                 sprintf(buf, "theta-av%d", g);
757             print_aligned(fp, buf);
758             sprintf(buf2, "%s [degrees]", buf);
759             setname[nsets] = strdup(buf2);
760             nsets++;
761
762             sprintf(buf, "tau%d", g);
763             print_aligned(fp, buf);
764             sprintf(buf2, "%s [kJ/mol]", buf);
765             setname[nsets] = strdup(buf2);
766             nsets++;
767
768             sprintf(buf, "energy%d", g);
769             print_aligned(fp, buf);
770             sprintf(buf2, "%s [kJ/mol]", buf);
771             setname[nsets] = strdup(buf2);
772             nsets++;
773         }
774         fprintf(fp, "\n#\n");
775         
776         if (nsets > 1)
777             xvgr_legend(fp, nsets, setname, oenv);
778         sfree(setname);
779         
780         fflush(fp);
781     }
782     
783     return fp;
784 }
785
786
787 /* Call on master only */
788 static FILE *open_angles_out(t_rot *rot, const char *fn)
789 {
790     int      g;
791     FILE     *fp=NULL;
792     t_rotgrp *rotg;
793
794
795     /* Open output file and write some information about it's structure: */
796     fp = open_output_file(fn, rot->nstsout, "rotation group angles");
797     fprintf(fp, "# All angles given in degrees, time in ps.\n");
798     for (g=0; g<rot->ngrp; g++)
799     {
800         rotg = &rot->grp[g];
801         if (ISFLEX(rotg))
802         {
803             fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, fit type %s.\n",
804                     g, erotg_names[rotg->eType], rotg->slab_dist, erotg_fitnames[rotg->eFittype]);
805         }
806     }
807     fprintf(fp, "# The following columns will have the syntax:\n");
808     fprintf(fp, "#     ");
809     print_aligned_short(fp, "t");
810     print_aligned_short(fp, "grp");
811     print_aligned(fp, "theta_ref");
812     print_aligned_short(fp, "slab");
813     print_aligned_short(fp, "atoms");
814     print_aligned(fp, "theta_fit");
815     print_aligned_short(fp, "slab");
816     print_aligned_short(fp, "atoms");
817     print_aligned(fp, "theta_fit");
818     fprintf(fp, " ...\n");
819     fflush(fp);
820     return fp;
821 }
822
823
824 /* Open torque output file and write some information about it's structure.
825  * Call on master only */
826 static FILE *open_torque_out(t_rot *rot, const char *fn)
827 {
828     FILE      *fp;
829     int       g;
830     t_rotgrp  *rotg;
831
832
833     fp = open_output_file(fn, rot->nstsout,"torques");
834
835     for (g=0; g<rot->ngrp; g++)
836     {
837         rotg = &rot->grp[g];
838         if (ISFLEX(rotg))
839         {
840             fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
841             fprintf(fp, "# The scalar tau is the torque [kJ/mol] in the direction of the rotation vector.\n");
842             fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
843             fprintf(fp, "# rot_vec%d            %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
844             fprintf(fp, "#\n");
845         }
846     }
847     fprintf(fp, "# The following columns will have the syntax (tau=torque for that slab):\n");
848     fprintf(fp, "#     ");
849     print_aligned_short(fp, "t");
850     print_aligned_short(fp, "grp");
851     print_aligned_short(fp, "slab");
852     print_aligned(fp, "tau");
853     print_aligned_short(fp, "slab");
854     print_aligned(fp, "tau");
855     fprintf(fp, " ...\n");
856     fflush(fp);
857
858     return fp;
859 }
860
861
862 static void swap_val(double* vec, int i, int j)
863 {
864     double tmp = vec[j];
865     
866     
867     vec[j]=vec[i];
868     vec[i]=tmp;
869 }
870
871
872 static void swap_col(double **mat, int i, int j)
873 {
874     double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
875     
876     
877     mat[0][j]=mat[0][i];
878     mat[1][j]=mat[1][i];
879     mat[2][j]=mat[2][i];
880     
881     mat[0][i]=tmp[0];
882     mat[1][i]=tmp[1];
883     mat[2][i]=tmp[2];
884
885
886
887 /* Eigenvectors are stored in columns of eigen_vec */
888 static void diagonalize_symmetric(
889         double **matrix,
890         double **eigen_vec,
891         double eigenval[3])
892 {
893     int n_rot;
894     
895     
896     jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
897     
898     /* sort in ascending order */
899     if (eigenval[0] > eigenval[1])
900     {
901         swap_val(eigenval, 0, 1);
902         swap_col(eigen_vec, 0, 1);
903     } 
904     if (eigenval[1] > eigenval[2])
905     {
906         swap_val(eigenval, 1, 2);
907         swap_col(eigen_vec, 1, 2);
908     }
909     if (eigenval[0] > eigenval[1])
910     {
911         swap_val(eigenval, 0, 1);
912         swap_col(eigen_vec, 0, 1);
913     }
914 }
915
916
917 static void align_with_z(
918         rvec* s,           /* Structure to align */
919         int natoms,
920         rvec axis)
921 {
922     int    i, j, k;
923     rvec   zet = {0.0, 0.0, 1.0};
924     rvec   rot_axis={0.0, 0.0, 0.0};
925     rvec   *rotated_str=NULL;
926     real   ooanorm;
927     real   angle;
928     matrix rotmat;
929     
930     
931     snew(rotated_str, natoms);
932
933     /* Normalize the axis */
934     ooanorm = 1.0/norm(axis);
935     svmul(ooanorm, axis, axis);
936     
937     /* Calculate the angle for the fitting procedure */
938     cprod(axis, zet, rot_axis);
939     angle = acos(axis[2]);
940     if (angle < 0.0)
941         angle += M_PI;
942     
943     /* Calculate the rotation matrix */
944     calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
945     
946     /* Apply the rotation matrix to s */
947     for (i=0; i<natoms; i++)
948     {    
949         for(j=0; j<3; j++)
950         {
951             for(k=0; k<3; k++)
952             {
953                 rotated_str[i][j] += rotmat[j][k]*s[i][k];
954             }
955         }
956     }
957     
958     /* Rewrite the rotated structure to s */
959     for(i=0; i<natoms; i++)
960     {
961         for(j=0; j<3; j++)
962         {
963             s[i][j]=rotated_str[i][j];
964         }
965     }
966     
967     sfree(rotated_str);
968
969
970
971 static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
972 {    
973     int i, j, k;
974  
975     
976     for (i=0; i<3; i++)
977         for (j=0; j<3; j++)
978             Rmat[i][j] = 0.0;
979     
980     for (i=0; i<3; i++) 
981         for (j=0; j<3; j++) 
982             for (k=0; k<natoms; k++) 
983                 Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
984 }
985
986
987 static void weigh_coords(rvec* str, real* weight, int natoms)
988 {
989     int i, j;
990     
991     
992     for(i=0; i<natoms; i++)
993     {
994         for(j=0; j<3; j++)
995             str[i][j] *= sqrt(weight[i]);
996     }  
997 }
998
999
1000 static real opt_angle_analytic(
1001         rvec* ref_s,
1002         rvec* act_s,
1003         real* weight, 
1004         int natoms,
1005         rvec ref_com,
1006         rvec act_com,
1007         rvec axis)
1008 {    
1009     int    i, j, k;
1010     rvec   *ref_s_1=NULL;
1011     rvec   *act_s_1=NULL;
1012     rvec   shift;
1013     double **Rmat, **RtR, **eigvec;
1014     double eigval[3];
1015     double V[3][3], WS[3][3];
1016     double rot_matrix[3][3];
1017     double opt_angle;
1018     
1019     
1020     /* Do not change the original coordinates */ 
1021     snew(ref_s_1, natoms);
1022     snew(act_s_1, natoms);
1023     for(i=0; i<natoms; i++)
1024     {
1025         copy_rvec(ref_s[i], ref_s_1[i]);
1026         copy_rvec(act_s[i], act_s_1[i]);
1027     }
1028     
1029     /* Translate the structures to the origin */
1030     shift[XX] = -ref_com[XX];
1031     shift[YY] = -ref_com[YY];
1032     shift[ZZ] = -ref_com[ZZ];
1033     translate_x(ref_s_1, natoms, shift);
1034     
1035     shift[XX] = -act_com[XX];
1036     shift[YY] = -act_com[YY];
1037     shift[ZZ] = -act_com[ZZ];
1038     translate_x(act_s_1, natoms, shift);
1039     
1040     /* Align rotation axis with z */
1041     align_with_z(ref_s_1, natoms, axis);
1042     align_with_z(act_s_1, natoms, axis);
1043     
1044     /* Correlation matrix */
1045     Rmat = allocate_square_matrix(3);
1046     
1047     for (i=0; i<natoms; i++)
1048     {
1049         ref_s_1[i][2]=0.0;
1050         act_s_1[i][2]=0.0;
1051     }
1052     
1053     /* Weight positions with sqrt(weight) */
1054     if (NULL != weight)
1055     {
1056         weigh_coords(ref_s_1, weight, natoms);
1057         weigh_coords(act_s_1, weight, natoms);
1058     }
1059     
1060     /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
1061     calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
1062     
1063     /* Calculate RtR */
1064     RtR = allocate_square_matrix(3);
1065     for (i=0; i<3; i++)
1066     {
1067         for (j=0; j<3; j++)
1068         {
1069             for (k=0; k<3; k++)
1070             {
1071                 RtR[i][j] += Rmat[k][i] * Rmat[k][j];
1072             }
1073         }
1074     }
1075     /* Diagonalize RtR */
1076     snew(eigvec,3);
1077     for (i=0; i<3; i++)
1078         snew(eigvec[i],3);
1079     
1080     diagonalize_symmetric(RtR, eigvec, eigval);
1081     swap_col(eigvec,0,1);
1082     swap_col(eigvec,1,2);
1083     swap_val(eigval,0,1);
1084     swap_val(eigval,1,2);
1085     
1086     /* Calculate V */
1087     for(i=0; i<3; i++)
1088     {
1089         for(j=0; j<3; j++)
1090         {
1091             V[i][j]  = 0.0;
1092             WS[i][j] = 0.0;
1093         }
1094     }
1095     
1096     for (i=0; i<2; i++)
1097         for (j=0; j<2; j++)
1098             WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
1099     
1100     for (i=0; i<3; i++)
1101     {
1102         for (j=0; j<3; j++)
1103         {
1104             for (k=0; k<3; k++)
1105             {
1106                 V[i][j] += Rmat[i][k]*WS[k][j];
1107             }
1108         }
1109     }
1110     free_square_matrix(Rmat, 3);
1111     
1112     /* Calculate optimal rotation matrix */
1113     for (i=0; i<3; i++)
1114         for (j=0; j<3; j++)
1115             rot_matrix[i][j] = 0.0;
1116     
1117     for (i=0; i<3; i++)
1118     {
1119         for(j=0; j<3; j++)
1120         {
1121             for(k=0; k<3; k++){
1122                 rot_matrix[i][j] += eigvec[i][k]*V[j][k];
1123             }
1124         }
1125     }
1126     rot_matrix[2][2] = 1.0;
1127         
1128     /* In some cases abs(rot_matrix[0][0]) can be slighly larger
1129      * than unity due to numerical inacurracies. To be able to calculate
1130      * the acos function, we put these values back in range. */
1131     if (rot_matrix[0][0] > 1.0)
1132     {
1133         rot_matrix[0][0] = 1.0;
1134     }
1135     else if (rot_matrix[0][0] < -1.0)
1136     {
1137         rot_matrix[0][0] = -1.0;
1138     }
1139
1140     /* Determine the optimal rotation angle: */
1141     opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
1142     if (rot_matrix[0][1] < 0.0)
1143         opt_angle = (-1.0)*opt_angle;
1144         
1145     /* Give back some memory */
1146     free_square_matrix(RtR, 3);
1147     sfree(ref_s_1);
1148     sfree(act_s_1);
1149     for (i=0; i<3; i++)
1150         sfree(eigvec[i]);
1151     sfree(eigvec);
1152     
1153     return (real) opt_angle;
1154 }
1155
1156
1157 /* Determine angle of the group by RMSD fit to the reference */
1158 /* Not parallelized, call this routine only on the master */
1159 static real flex_fit_angle(t_rotgrp *rotg)
1160 {
1161     int         i;
1162     rvec        *fitcoords=NULL;
1163     rvec        center;         /* Center of positions passed to the fit routine */
1164     real        fitangle;       /* Angle of the rotation group derived by fitting */
1165     rvec        coord;
1166     real        scal;
1167     gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
1168
1169     
1170     erg=rotg->enfrotgrp;
1171
1172     /* Get the center of the rotation group.
1173      * Note, again, erg->xc has been sorted in do_flexible */
1174     get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
1175
1176     /* === Determine the optimal fit angle for the rotation group === */
1177     if (rotg->eFittype == erotgFitNORM)
1178     {
1179         /* Normalize every position to it's reference length */
1180         for (i=0; i<rotg->nat; i++)
1181         {
1182             /* Put the center of the positions into the origin */
1183             rvec_sub(erg->xc[i], center, coord);
1184             /* Determine the scaling factor for the length: */
1185             scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
1186             /* Get position, multiply with the scaling factor and save  */
1187             svmul(scal, coord, erg->xc_norm[i]);
1188         }
1189         fitcoords = erg->xc_norm;
1190     }
1191     else
1192     {
1193         fitcoords = erg->xc;
1194     }
1195     /* From the point of view of the current positions, the reference has rotated
1196      * backwards. Since we output the angle relative to the fixed reference,
1197      * we need the minus sign. */
1198     fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
1199                                    rotg->nat, erg->xc_ref_center, center, rotg->vec);
1200
1201     return fitangle;
1202 }
1203
1204
1205 /* Determine actual angle of each slab by RMSD fit to the reference */
1206 /* Not parallelized, call this routine only on the master */
1207 static void flex_fit_angle_perslab(
1208         int  g,
1209         t_rotgrp *rotg,
1210         double t,
1211         real degangle,
1212         FILE *fp)
1213 {
1214     int         i,l,n,islab,ind;
1215     rvec        curr_x, ref_x;
1216     rvec        act_center;  /* Center of actual positions that are passed to the fit routine */
1217     rvec        ref_center;  /* Same for the reference positions */
1218     real        fitangle;    /* Angle of a slab derived from an RMSD fit to
1219                               * the reference structure at t=0  */
1220     t_gmx_slabdata *sd;
1221     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
1222     real        OOm_av;      /* 1/average_mass of a rotation group atom */
1223     real        m_rel;       /* Relative mass of a rotation group atom  */
1224
1225
1226     erg=rotg->enfrotgrp;
1227
1228     /* Average mass of a rotation group atom: */
1229     OOm_av = erg->invmass*rotg->nat;
1230
1231     /**********************************/
1232     /* First collect the data we need */
1233     /**********************************/
1234
1235     /* Collect the data for the individual slabs */
1236     for (n = erg->slab_first; n <= erg->slab_last; n++)
1237     {
1238         islab = n - erg->slab_first; /* slab index */
1239         sd = &(rotg->enfrotgrp->slab_data[islab]);
1240         sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
1241         ind = 0;
1242
1243         /* Loop over the relevant atoms in the slab */
1244         for (l=erg->firstatom[islab]; l<=erg->lastatom[islab]; l++)
1245         {
1246             /* Current position of this atom: x[ii][XX/YY/ZZ] */
1247             copy_rvec(erg->xc[l], curr_x);
1248
1249             /* The (unrotated) reference position of this atom is copied to ref_x.
1250              * Beware, the xc coords have been sorted in do_flexible */
1251             copy_rvec(erg->xc_ref_sorted[l], ref_x);
1252
1253             /* Save data for doing angular RMSD fit later */
1254             /* Save the current atom position */
1255             copy_rvec(curr_x, sd->x[ind]);
1256             /* Save the corresponding reference position */
1257             copy_rvec(ref_x , sd->ref[ind]);
1258
1259             /* Maybe also mass-weighting was requested. If yes, additionally
1260              * multiply the weights with the relative mass of the atom. If not,
1261              * multiply with unity. */
1262             m_rel = erg->mc_sorted[l]*OOm_av;
1263
1264             /* Save the weight for this atom in this slab */
1265             sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
1266
1267             /* Next atom in this slab */
1268             ind++;
1269         }
1270     }
1271
1272     /******************************/
1273     /* Now do the fit calculation */
1274     /******************************/
1275
1276     fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
1277
1278     /* === Now do RMSD fitting for each slab === */
1279     /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
1280 #define SLAB_MIN_ATOMS 4
1281
1282     for (n = erg->slab_first; n <= erg->slab_last; n++)
1283     {
1284         islab = n - erg->slab_first; /* slab index */
1285         sd = &(rotg->enfrotgrp->slab_data[islab]);
1286         if (sd->nat >= SLAB_MIN_ATOMS)
1287         {
1288             /* Get the center of the slabs reference and current positions */
1289             get_center(sd->ref, sd->weight, sd->nat, ref_center);
1290             get_center(sd->x  , sd->weight, sd->nat, act_center);
1291             if (rotg->eFittype == erotgFitNORM)
1292             {
1293                 /* Normalize every position to it's reference length
1294                  * prior to performing the fit */
1295                 for (i=0; i<sd->nat;i++) /* Center */
1296                 {
1297                     rvec_dec(sd->ref[i], ref_center);
1298                     rvec_dec(sd->x[i]  , act_center);
1299                     /* Normalize x_i such that it gets the same length as ref_i */
1300                     svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
1301                 }
1302                 /* We already subtracted the centers */
1303                 clear_rvec(ref_center);
1304                 clear_rvec(act_center);
1305             }
1306             fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
1307                                            ref_center, act_center, rotg->vec);
1308             fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
1309         }
1310     }
1311     fprintf(fp     , "\n");
1312
1313 #undef SLAB_MIN_ATOMS
1314 }
1315
1316
1317 /* Shift x with is */
1318 static inline void shift_single_coord(matrix box, rvec x, const ivec is)
1319 {
1320     int tx,ty,tz;
1321
1322
1323     tx=is[XX];
1324     ty=is[YY];
1325     tz=is[ZZ];
1326
1327     if(TRICLINIC(box))
1328     {
1329         x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
1330         x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
1331         x[ZZ] += tz*box[ZZ][ZZ];
1332     } else
1333     {
1334         x[XX] += tx*box[XX][XX];
1335         x[YY] += ty*box[YY][YY];
1336         x[ZZ] += tz*box[ZZ][ZZ];
1337     }
1338 }
1339
1340
1341 /* Determine the 'home' slab of this atom which is the
1342  * slab with the highest Gaussian weight of all */
1343 #define round(a) (int)(a+0.5)
1344 static inline int get_homeslab(
1345         rvec curr_x,   /* The position for which the home slab shall be determined */ 
1346         rvec rotvec,   /* The rotation vector */
1347         real slabdist) /* The slab distance */
1348 {
1349     real dist;
1350     
1351     
1352     /* The distance of the atom to the coordinate center (where the
1353      * slab with index 0) is */
1354     dist = iprod(rotvec, curr_x);
1355     
1356     return round(dist / slabdist); 
1357 }
1358
1359
1360 /* For a local atom determine the relevant slabs, i.e. slabs in
1361  * which the gaussian is larger than min_gaussian
1362  */
1363 static int get_single_atom_gaussians(
1364         rvec      curr_x,
1365         t_commrec *cr,
1366         t_rotgrp  *rotg)
1367 {
1368    int slab, homeslab;
1369    real g;
1370    int count = 0;
1371    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
1372
1373    
1374    erg=rotg->enfrotgrp;
1375    
1376    /* Determine the 'home' slab of this atom: */
1377    homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
1378
1379    /* First determine the weight in the atoms home slab: */
1380    g = gaussian_weight(curr_x, rotg, homeslab);
1381    
1382    erg->gn_atom[count] = g;
1383    erg->gn_slabind[count] = homeslab;
1384    count++;
1385    
1386    
1387    /* Determine the max slab */
1388    slab = homeslab;
1389    while (g > rotg->min_gaussian)
1390    {
1391        slab++;
1392        g = gaussian_weight(curr_x, rotg, slab);
1393        erg->gn_slabind[count]=slab;
1394        erg->gn_atom[count]=g;
1395        count++;
1396    }
1397    count--;
1398    
1399    /* Determine the max slab */
1400    slab = homeslab;
1401    do
1402    {
1403        slab--;
1404        g = gaussian_weight(curr_x, rotg, slab);       
1405        erg->gn_slabind[count]=slab;
1406        erg->gn_atom[count]=g;
1407        count++;
1408    }
1409    while (g > rotg->min_gaussian);
1410    count--;
1411    
1412    return count;
1413 }
1414
1415
1416 static void flex2_precalc_inner_sum(t_rotgrp *rotg, t_commrec *cr)
1417 {
1418     int  i,n,islab;
1419     rvec  xi;                /* positions in the i-sum                        */
1420     rvec  xcn, ycn;          /* the current and the reference slab centers    */
1421     real gaussian_xi;
1422     rvec yi0;
1423     rvec  rin;               /* Helper variables                              */
1424     real  fac,fac2;
1425     rvec innersumvec;
1426     real OOpsii,OOpsiistar;
1427     real sin_rin;          /* s_ii.r_ii */
1428     rvec s_in,tmpvec,tmpvec2;
1429     real mi,wi;            /* Mass-weighting of the positions                 */
1430     real N_M;              /* N/M                                             */
1431     gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
1432
1433
1434     erg=rotg->enfrotgrp;
1435     N_M = rotg->nat * erg->invmass;
1436
1437     /* Loop over all slabs that contain something */
1438     for (n=erg->slab_first; n <= erg->slab_last; n++)
1439     {
1440         islab = n - erg->slab_first; /* slab index */
1441
1442         /* The current center of this slab is saved in xcn: */
1443         copy_rvec(erg->slab_center[islab], xcn);
1444         /* ... and the reference center in ycn: */
1445         copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1446
1447         /*** D. Calculate the whole inner sum used for second and third sum */
1448         /* For slab n, we need to loop over all atoms i again. Since we sorted
1449          * the atoms with respect to the rotation vector, we know that it is sufficient
1450          * to calculate from firstatom to lastatom only. All other contributions will
1451          * be very small. */
1452         clear_rvec(innersumvec);
1453         for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
1454         {
1455             /* Coordinate xi of this atom */
1456             copy_rvec(erg->xc[i],xi);
1457
1458             /* The i-weights */
1459             gaussian_xi = gaussian_weight(xi,rotg,n);
1460             mi = erg->mc_sorted[i];  /* need the sorted mass here */
1461             wi = N_M*mi;
1462
1463             /* Calculate rin */
1464             copy_rvec(erg->xc_ref_sorted[i],yi0); /* Reference position yi0   */
1465             rvec_sub(yi0, ycn, tmpvec2);          /* tmpvec2 = yi0 - ycn      */
1466             mvmul(erg->rotmat, tmpvec2, rin);     /* rin = Omega.(yi0 - ycn)  */
1467
1468             /* Calculate psi_i* and sin */
1469             rvec_sub(xi, xcn, tmpvec2);           /* tmpvec2 = xi - xcn       */
1470             cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xi - xcn)  */
1471             OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
1472             OOpsii = norm(tmpvec);                /* OOpsii = 1 / psii = |v x (xi - xcn)| */
1473
1474                                        /*         v x (xi - xcn)          */
1475             unitv(tmpvec, s_in);       /*  sin = ----------------         */
1476                                        /*        |v x (xi - xcn)|         */
1477
1478             sin_rin=iprod(s_in,rin);   /* sin_rin = sin . rin             */
1479
1480             /* Now the whole sum */
1481             fac = OOpsii/OOpsiistar;
1482             svmul(fac, rin, tmpvec);
1483             fac2 = fac*fac*OOpsii;
1484             svmul(fac2*sin_rin, s_in, tmpvec2);
1485             rvec_dec(tmpvec, tmpvec2);
1486
1487             svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
1488
1489             rvec_inc(innersumvec,tmpvec2);
1490         } /* now we have the inner sum, used both for sum2 and sum3 */
1491
1492         /* Save it to be used in do_flex2_lowlevel */
1493         copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
1494     } /* END of loop over slabs */
1495 }
1496
1497
1498 static void flex_precalc_inner_sum(t_rotgrp *rotg, t_commrec *cr)
1499 {
1500     int   i,n,islab;
1501     rvec  xi;                /* position                                      */
1502     rvec  xcn, ycn;          /* the current and the reference slab centers    */
1503     rvec  qin,rin;           /* q_i^n and r_i^n                               */
1504     real  bin;
1505     rvec  tmpvec;
1506     rvec  innersumvec;       /* Inner part of sum_n2                          */
1507     real  gaussian_xi;       /* Gaussian weight gn(xi)                        */
1508     real  mi,wi;             /* Mass-weighting of the positions               */
1509     real  N_M;               /* N/M                                           */
1510
1511     gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
1512
1513
1514     erg=rotg->enfrotgrp;
1515     N_M = rotg->nat * erg->invmass;
1516
1517     /* Loop over all slabs that contain something */
1518     for (n=erg->slab_first; n <= erg->slab_last; n++)
1519     {
1520         islab = n - erg->slab_first; /* slab index */
1521
1522         /* The current center of this slab is saved in xcn: */
1523         copy_rvec(erg->slab_center[islab], xcn);
1524         /* ... and the reference center in ycn: */
1525         copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1526
1527         /* For slab n, we need to loop over all atoms i again. Since we sorted
1528          * the atoms with respect to the rotation vector, we know that it is sufficient
1529          * to calculate from firstatom to lastatom only. All other contributions will
1530          * be very small. */
1531         clear_rvec(innersumvec);
1532         for (i=erg->firstatom[islab]; i<=erg->lastatom[islab]; i++)
1533         {
1534             /* Coordinate xi of this atom */
1535             copy_rvec(erg->xc[i],xi);
1536
1537             /* The i-weights */
1538             gaussian_xi = gaussian_weight(xi,rotg,n);
1539             mi = erg->mc_sorted[i];  /* need the sorted mass here */
1540             wi = N_M*mi;
1541
1542             /* Calculate rin and qin */
1543             rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
1544             mvmul(erg->rotmat, tmpvec, rin);      /* rin = Omega.(yi0 - ycn)  */
1545             cprod(rotg->vec, rin, tmpvec);    /* tmpvec = v x Omega*(yi0-ycn) */
1546
1547                                              /*        v x Omega*(yi0-ycn)    */
1548             unitv(tmpvec, qin);              /* qin = ---------------------   */
1549                                              /*       |v x Omega*(yi0-ycn)|   */
1550
1551             /* Calculate bin */
1552             rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
1553             bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
1554
1555             svmul(wi*gaussian_xi*bin, qin, tmpvec);
1556
1557             /* Add this contribution to the inner sum: */
1558             rvec_add(innersumvec, tmpvec, innersumvec);
1559         } /* now we have the inner sum vector S^n for this slab */
1560         /* Save it to be used in do_flex_lowlevel */
1561         copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
1562     }
1563 }
1564
1565
1566 static real do_flex2_lowlevel(
1567         t_rotgrp  *rotg,
1568         real      sigma,    /* The Gaussian width sigma */
1569         rvec      x[],
1570         gmx_bool  bCalcTorque,
1571         matrix    box,
1572         t_commrec *cr)
1573 {
1574     int  count,ic,ii,j,m,n,islab,iigrp;
1575     rvec  xj;                /* position in the i-sum                         */
1576     rvec  yj0;               /* the reference position in the j-sum           */
1577     rvec  xcn, ycn;          /* the current and the reference slab centers    */
1578     real V;                  /* This node's part of the rotation pot. energy  */
1579     real gaussian_xj;        /* Gaussian weight                               */
1580     real beta;
1581
1582     real  numerator;
1583     rvec  rjn;               /* Helper variables                              */
1584     real  fac,fac2;
1585
1586     real OOpsij,OOpsijstar;
1587     real OOsigma2;           /* 1/(sigma^2)                                   */
1588     real sjn_rjn;
1589     real betasigpsi;
1590     rvec sjn,tmpvec,tmpvec2;
1591     rvec sum1vec_part,sum1vec,sum2vec_part,sum2vec,sum3vec,sum4vec,innersumvec;
1592     real sum3,sum4;
1593     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
1594     real mj,wj;              /* Mass-weighting of the positions               */
1595     real N_M;                /* N/M                                           */
1596     real Wjn;                /* g_n(x_j) m_j / Mjn                            */
1597
1598     /* To calculate the torque per slab */
1599     rvec slab_force;         /* Single force from slab n on one atom          */
1600     rvec slab_sum1vec_part;
1601     real slab_sum3part,slab_sum4part;
1602     rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
1603
1604
1605     erg=rotg->enfrotgrp;
1606
1607     /* Pre-calculate the inner sums, so that we do not have to calculate
1608      * them again for every atom */
1609     flex2_precalc_inner_sum(rotg, cr);
1610
1611     /********************************************************/
1612     /* Main loop over all local atoms of the rotation group */
1613     /********************************************************/
1614     N_M = rotg->nat * erg->invmass;
1615     V = 0.0;
1616     OOsigma2 = 1.0 / (sigma*sigma);
1617     for (j=0; j<erg->nat_loc; j++)
1618     {
1619         /* Local index of a rotation group atom  */
1620         ii = erg->ind_loc[j];
1621         /* Position of this atom in the collective array */
1622         iigrp = erg->xc_ref_ind[j];
1623         /* Mass-weighting */
1624         mj = erg->mc[iigrp];  /* need the unsorted mass here */
1625         wj = N_M*mj;
1626         
1627         /* Current position of this atom: x[ii][XX/YY/ZZ]
1628          * Note that erg->xc_center contains the center of mass in case the flex2-t
1629          * potential was chosen. For the flex2 potential erg->xc_center must be
1630          * zero. */
1631         rvec_sub(x[ii], erg->xc_center, xj);
1632
1633         /* Shift this atom such that it is near its reference */
1634         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
1635
1636         /* Determine the slabs to loop over, i.e. the ones with contributions
1637          * larger than min_gaussian */
1638         count = get_single_atom_gaussians(xj, cr, rotg);
1639         
1640         clear_rvec(sum1vec_part);
1641         clear_rvec(sum2vec_part);
1642         sum3 = 0.0;
1643         sum4 = 0.0;
1644         /* Loop over the relevant slabs for this atom */
1645         for (ic=0; ic < count; ic++)  
1646         {
1647             n = erg->gn_slabind[ic];
1648             
1649             /* Get the precomputed Gaussian value of curr_slab for curr_x */
1650             gaussian_xj = erg->gn_atom[ic];
1651
1652             islab = n - erg->slab_first; /* slab index */
1653             
1654             /* The (unrotated) reference position of this atom is copied to yj0: */
1655             copy_rvec(rotg->x_ref[iigrp], yj0);
1656
1657             beta = calc_beta(xj, rotg,n);
1658
1659             /* The current center of this slab is saved in xcn: */
1660             copy_rvec(erg->slab_center[islab], xcn);
1661             /* ... and the reference center in ycn: */
1662             copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1663             
1664             rvec_sub(yj0, ycn, tmpvec2);          /* tmpvec2 = yj0 - ycn      */
1665
1666             /* Rotate: */
1667             mvmul(erg->rotmat, tmpvec2, rjn);     /* rjn = Omega.(yj0 - ycn)  */
1668             
1669             /* Subtract the slab center from xj */
1670             rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
1671
1672             /* Calculate sjn */
1673             cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xj - xcn)  */
1674
1675             OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
1676
1677             numerator = sqr(iprod(tmpvec, rjn));
1678             
1679             /*********************************/
1680             /* Add to the rotation potential */
1681             /*********************************/
1682             V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
1683
1684
1685             /*************************************/
1686             /* Now calculate the force on atom j */
1687             /*************************************/
1688
1689             OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
1690
1691                                            /*         v x (xj - xcn)          */
1692             unitv(tmpvec, sjn);            /*  sjn = ----------------         */
1693                                            /*        |v x (xj - xcn)|         */
1694
1695             sjn_rjn=iprod(sjn,rjn);        /* sjn_rjn = sjn . rjn             */
1696
1697
1698             /*** A. Calculate the first of the four sum terms: ****************/
1699             fac = OOpsij/OOpsijstar;
1700             svmul(fac, rjn, tmpvec);
1701             fac2 = fac*fac*OOpsij;
1702             svmul(fac2*sjn_rjn, sjn, tmpvec2);
1703             rvec_dec(tmpvec, tmpvec2);
1704             fac2 = wj*gaussian_xj; /* also needed for sum4 */
1705             svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
1706             /********************/
1707             /*** Add to sum1: ***/
1708             /********************/
1709             rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
1710
1711             /*** B. Calculate the forth of the four sum terms: ****************/
1712             betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
1713             /********************/
1714             /*** Add to sum4: ***/
1715             /********************/
1716             slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
1717             sum4 += slab_sum4part;
1718
1719             /*** C. Calculate Wjn for second and third sum */
1720             /* Note that we can safely divide by slab_weights since we check in
1721              * get_slab_centers that it is non-zero. */
1722             Wjn = gaussian_xj*mj/erg->slab_weights[islab];
1723
1724             /* We already have precalculated the inner sum for slab n */
1725             copy_rvec(erg->slab_innersumvec[islab], innersumvec);
1726
1727             /* Weigh the inner sum vector with Wjn */
1728             svmul(Wjn, innersumvec, innersumvec);
1729
1730             /*** E. Calculate the second of the four sum terms: */
1731             /********************/
1732             /*** Add to sum2: ***/
1733             /********************/
1734             rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
1735             
1736             /*** F. Calculate the third of the four sum terms: */
1737             slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
1738             sum3 += slab_sum3part; /* still needs to be multiplied with v */
1739
1740             /*** G. Calculate the torque on the local slab's axis: */
1741             if (bCalcTorque)
1742             {
1743                 /* Sum1 */
1744                 cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
1745                 /* Sum2 */
1746                 cprod(innersumvec, rotg->vec, slab_sum2vec);
1747                 /* Sum3 */
1748                 svmul(slab_sum3part, rotg->vec, slab_sum3vec);
1749                 /* Sum4 */
1750                 svmul(slab_sum4part, rotg->vec, slab_sum4vec);
1751
1752                 /* The force on atom ii from slab n only: */
1753                 for (m=0; m<DIM; m++)
1754                     slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
1755
1756                 erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
1757             }
1758         } /* END of loop over slabs */
1759
1760         /* Construct the four individual parts of the vector sum: */
1761         cprod(sum1vec_part, rotg->vec, sum1vec);      /* sum1vec =   { } x v  */
1762         cprod(sum2vec_part, rotg->vec, sum2vec);      /* sum2vec =   { } x v  */
1763         svmul(sum3, rotg->vec, sum3vec);              /* sum3vec =   { } . v  */
1764         svmul(sum4, rotg->vec, sum4vec);              /* sum4vec =   { } . v  */
1765
1766         /* Store the additional force so that it can be added to the force
1767          * array after the normal forces have been evaluated */
1768         for (m=0; m<DIM; m++)
1769             erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
1770
1771 #ifdef SUM_PARTS
1772         fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n",    -rotg->k*sum1vec[XX],    -rotg->k*sum1vec[YY],    -rotg->k*sum1vec[ZZ]);
1773         fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n",     rotg->k*sum2vec[XX],     rotg->k*sum2vec[YY],     rotg->k*sum2vec[ZZ]);
1774         fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n",    -rotg->k*sum3vec[XX],    -rotg->k*sum3vec[YY],    -rotg->k*sum3vec[ZZ]);
1775         fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*rotg->k*sum4vec[XX], 0.5*rotg->k*sum4vec[YY], 0.5*rotg->k*sum4vec[ZZ]);
1776 #endif
1777
1778         PRINT_FORCE_J
1779
1780     } /* END of loop over local atoms */
1781
1782     return V;
1783 }
1784
1785
1786 static real do_flex_lowlevel(
1787         t_rotgrp *rotg,
1788         real      sigma,     /* The Gaussian width sigma                      */
1789         rvec      x[],
1790         gmx_bool  bCalcTorque,
1791         matrix    box,
1792         t_commrec *cr)
1793 {
1794     int   count,ic,ii,j,m,n,islab,iigrp;
1795     rvec  xj,yj0;            /* current and reference position                */
1796     rvec  xcn, ycn;          /* the current and the reference slab centers    */
1797     rvec  xj_xcn;            /* xj - xcn                                      */
1798     rvec  qjn;               /* q_i^n                                         */
1799     rvec  sum_n1,sum_n2;     /* Two contributions to the rotation force       */
1800     rvec  innersumvec;       /* Inner part of sum_n2                          */
1801     rvec  s_n;
1802     rvec  force_n;           /* Single force from slab n on one atom          */
1803     rvec  force_n1,force_n2; /* First and second part of force_n              */
1804     rvec  tmpvec,tmpvec2,tmp_f;   /* Helper variables                         */
1805     real  V;                 /* The rotation potential energy                 */
1806     real  OOsigma2;          /* 1/(sigma^2)                                   */
1807     real  beta;              /* beta_n(xj)                                    */
1808     real  bjn;               /* b_j^n                                         */
1809     real  gaussian_xj;       /* Gaussian weight gn(xj)                        */
1810     real  betan_xj_sigma2;
1811     real  mj,wj;             /* Mass-weighting of the positions               */
1812     real  N_M;               /* N/M                                           */
1813     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
1814
1815     
1816     erg=rotg->enfrotgrp;
1817
1818     /* Pre-calculate the inner sums, so that we do not have to calculate
1819      * them again for every atom */
1820     flex_precalc_inner_sum(rotg, cr);
1821
1822     /********************************************************/
1823     /* Main loop over all local atoms of the rotation group */
1824     /********************************************************/
1825     OOsigma2 = 1.0/(sigma*sigma);
1826     N_M = rotg->nat * erg->invmass;
1827     V = 0.0;
1828     for (j=0; j<erg->nat_loc; j++)
1829     {
1830         /* Local index of a rotation group atom  */
1831         ii = erg->ind_loc[j];
1832         /* Position of this atom in the collective array */
1833         iigrp = erg->xc_ref_ind[j];
1834         /* Mass-weighting */
1835         mj = erg->mc[iigrp];  /* need the unsorted mass here */
1836         wj = N_M*mj;
1837         
1838         /* Current position of this atom: x[ii][XX/YY/ZZ]
1839          * Note that erg->xc_center contains the center of mass in case the flex-t
1840          * potential was chosen. For the flex potential erg->xc_center must be
1841          * zero. */
1842         rvec_sub(x[ii], erg->xc_center, xj);
1843         
1844         /* Shift this atom such that it is near its reference */
1845         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
1846
1847         /* Determine the slabs to loop over, i.e. the ones with contributions
1848          * larger than min_gaussian */
1849         count = get_single_atom_gaussians(xj, cr, rotg);
1850
1851         clear_rvec(sum_n1);
1852         clear_rvec(sum_n2);
1853
1854         /* Loop over the relevant slabs for this atom */
1855         for (ic=0; ic < count; ic++)  
1856         {
1857             n = erg->gn_slabind[ic];
1858                 
1859             /* Get the precomputed Gaussian for xj in slab n */
1860             gaussian_xj = erg->gn_atom[ic];
1861
1862             islab = n - erg->slab_first; /* slab index */
1863             
1864             /* The (unrotated) reference position of this atom is saved in yj0: */
1865             copy_rvec(rotg->x_ref[iigrp], yj0);
1866
1867             beta = calc_beta(xj, rotg, n);
1868
1869             /* The current center of this slab is saved in xcn: */
1870             copy_rvec(erg->slab_center[islab], xcn);
1871             /* ... and the reference center in ycn: */
1872             copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1873             
1874             rvec_sub(yj0, ycn, tmpvec); /* tmpvec = yj0 - ycn */
1875
1876             /* Rotate: */
1877             mvmul(erg->rotmat, tmpvec, tmpvec2); /* tmpvec2 = Omega.(yj0-ycn) */
1878             
1879             /* Subtract the slab center from xj */
1880             rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
1881             
1882             /* Calculate qjn */
1883             cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(xj-xcn) */
1884
1885                                  /*         v x Omega.(xj-xcn)    */
1886             unitv(tmpvec,qjn);   /*  qjn = --------------------   */
1887                                  /*        |v x Omega.(xj-xcn)|   */
1888
1889             bjn = iprod(qjn, xj_xcn);   /* bjn = qjn * (xj - xcn) */
1890             
1891             /*********************************/
1892             /* Add to the rotation potential */
1893             /*********************************/
1894             V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
1895             
1896             /****************************************************************/
1897             /* sum_n1 will typically be the main contribution to the force: */
1898             /****************************************************************/
1899             betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
1900
1901             /* The next lines calculate
1902              *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
1903             svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
1904             rvec_sub(qjn,tmpvec2,tmpvec);
1905
1906             /* Multiply with gn(xj)*bjn: */
1907             svmul(gaussian_xj*bjn,tmpvec,tmpvec2);
1908
1909             /* Sum over n: */
1910             rvec_inc(sum_n1,tmpvec2);
1911             
1912             /* We already have precalculated the Sn term for slab n */
1913             copy_rvec(erg->slab_innersumvec[islab], s_n);
1914                                                                           /*          beta_n(xj)              */
1915             svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
1916                                                                           /*            sigma^2               */
1917
1918             rvec_sub(s_n, tmpvec, innersumvec);
1919             
1920             /* We can safely divide by slab_weights since we check in get_slab_centers
1921              * that it is non-zero. */
1922             svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
1923
1924             rvec_add(sum_n2, innersumvec, sum_n2);
1925             
1926             GMX_MPE_LOG(ev_inner_loop_finish);
1927
1928             /* Calculate the torque: */
1929             if (bCalcTorque)
1930             {
1931                 /* The force on atom ii from slab n only: */
1932                 svmul(-rotg->k*wj, tmpvec2    , force_n1); /* part 1 */
1933                 svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
1934                 rvec_add(force_n1, force_n2, force_n);
1935                 erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
1936             }
1937         } /* END of loop over slabs */
1938
1939         /* Put both contributions together: */
1940         svmul(wj, sum_n1, sum_n1);
1941         svmul(mj, sum_n2, sum_n2);
1942         rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
1943
1944         /* Store the additional force so that it can be added to the force
1945          * array after the normal forces have been evaluated */
1946         for(m=0; m<DIM; m++)
1947             erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
1948
1949         PRINT_FORCE_J
1950
1951     } /* END of loop over local atoms */
1952
1953     return V;
1954 }
1955
1956 #ifdef PRINT_COORDS
1957 static void print_coordinates(t_commrec *cr, t_rotgrp *rotg, rvec x[], matrix box, int step)
1958 {
1959     int i;
1960     static FILE *fp;
1961     static char buf[STRLEN];
1962     static gmx_bool bFirst=1;
1963
1964
1965     if (bFirst)
1966     {
1967         sprintf(buf, "coords%d.txt", cr->nodeid);
1968         fp = fopen(buf, "w");
1969         bFirst = 0;
1970     }
1971
1972     fprintf(fp, "\nStep %d\n", step);
1973     fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
1974             box[XX][XX], box[XX][YY], box[XX][ZZ],
1975             box[YY][XX], box[YY][YY], box[YY][ZZ],
1976             box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
1977     for (i=0; i<rotg->nat; i++)
1978     {
1979         fprintf(fp, "%4d  %f %f %f\n", i,
1980                 erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
1981     }
1982     fflush(fp);
1983
1984 }
1985 #endif
1986
1987
1988 static int projection_compare(const void *a, const void *b)
1989 {
1990     sort_along_vec_t *xca, *xcb;
1991     
1992     
1993     xca = (sort_along_vec_t *)a;
1994     xcb = (sort_along_vec_t *)b;
1995     
1996     if (xca->xcproj < xcb->xcproj)
1997         return -1;
1998     else if (xca->xcproj > xcb->xcproj)
1999         return 1;
2000     else
2001         return 0;
2002 }
2003
2004
2005 static void sort_collective_coordinates(
2006         t_rotgrp *rotg,         /* Rotation group */
2007         sort_along_vec_t *data) /* Buffer for sorting the positions */
2008 {
2009     int i;
2010     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2011
2012     
2013     erg=rotg->enfrotgrp;
2014     
2015     /* The projection of the position vector on the rotation vector is
2016      * the relevant value for sorting. Fill the 'data' structure */
2017     for (i=0; i<rotg->nat; i++)
2018     {
2019         data[i].xcproj = iprod(erg->xc[i], rotg->vec);  /* sort criterium */
2020         data[i].m      = erg->mc[i];
2021         data[i].ind    = i;
2022         copy_rvec(erg->xc[i]    , data[i].x    );
2023         copy_rvec(rotg->x_ref[i], data[i].x_ref);
2024     }
2025     /* Sort the 'data' structure */
2026     gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
2027     
2028     /* Copy back the sorted values */
2029     for (i=0; i<rotg->nat; i++)
2030     {
2031         copy_rvec(data[i].x    , erg->xc[i]           );
2032         copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
2033         erg->mc_sorted[i]  = data[i].m;
2034         erg->xc_sortind[i] = data[i].ind;
2035     }
2036 }
2037
2038
2039 /* For each slab, get the first and the last index of the sorted atom
2040  * indices */
2041 static void get_firstlast_atom_per_slab(t_rotgrp *rotg, t_commrec *cr)
2042 {
2043     int i,islab,n;
2044     real beta;
2045     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
2046
2047     
2048     erg=rotg->enfrotgrp;
2049
2050     GMX_MPE_LOG(ev_get_firstlast_start);
2051     
2052     /* Find the first atom that needs to enter the calculation for each slab */
2053     n = erg->slab_first;  /* slab */
2054     i = 0; /* start with the first atom */
2055     do
2056     {
2057         /* Find the first atom that significantly contributes to this slab */
2058         do /* move forward in position until a large enough beta is found */
2059         {
2060             beta = calc_beta(erg->xc[i], rotg, n);
2061             i++;
2062         } while ((beta < -erg->max_beta) && (i < rotg->nat));
2063         i--;
2064         islab = n - erg->slab_first;  /* slab index */
2065         erg->firstatom[islab] = i;
2066         /* Proceed to the next slab */
2067         n++;
2068     } while (n <= erg->slab_last);
2069     
2070     /* Find the last atom for each slab */
2071      n = erg->slab_last; /* start with last slab */
2072      i = rotg->nat-1;  /* start with the last atom */
2073      do
2074      {
2075          do /* move backward in position until a large enough beta is found */
2076          {
2077              beta = calc_beta(erg->xc[i], rotg, n);
2078              i--;
2079          } while ((beta > erg->max_beta) && (i > -1));
2080          i++;
2081          islab = n - erg->slab_first;  /* slab index */
2082          erg->lastatom[islab] = i;
2083          /* Proceed to the next slab */
2084          n--;
2085      } while (n >= erg->slab_first);
2086     
2087      GMX_MPE_LOG(ev_get_firstlast_finish);
2088 }
2089
2090
2091 /* Determine the very first and very last slab that needs to be considered 
2092  * For the first slab that needs to be considered, we have to find the smallest
2093  * n that obeys:
2094  * 
2095  * x_first * v - n*Delta_x <= beta_max
2096  * 
2097  * slab index n, slab distance Delta_x, rotation vector v. For the last slab we 
2098  * have to find the largest n that obeys
2099  * 
2100  * x_last * v - n*Delta_x >= -beta_max
2101  *  
2102  */
2103 static inline int get_first_slab(
2104         t_rotgrp *rotg,     /* The rotation group (inputrec data) */
2105         real     max_beta,  /* The max_beta value, instead of min_gaussian */
2106         rvec     firstatom) /* First atom after sorting along the rotation vector v */
2107 {
2108     /* Find the first slab for the first atom */   
2109     return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
2110 }
2111
2112
2113 static inline int get_last_slab(
2114         t_rotgrp *rotg,     /* The rotation group (inputrec data) */
2115         real     max_beta,  /* The max_beta value, instead of min_gaussian */
2116         rvec     lastatom)  /* Last atom along v */
2117 {
2118     /* Find the last slab for the last atom */
2119     return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);    
2120 }
2121
2122
2123 static void get_firstlast_slab_check(
2124         t_rotgrp        *rotg,     /* The rotation group (inputrec data) */
2125         t_gmx_enfrotgrp *erg,      /* The rotation group (data only accessible in this file) */
2126         rvec            firstatom, /* First atom after sorting along the rotation vector v */
2127         rvec            lastatom,  /* Last atom along v */
2128         int             g,         /* The rotation group number */
2129         t_commrec       *cr)
2130 {
2131     erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
2132     erg->slab_last  = get_last_slab(rotg, erg->max_beta, lastatom);
2133
2134     /* Check whether we have reference data to compare against */
2135     if (erg->slab_first < erg->slab_first_ref)
2136         gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
2137                   RotStr, erg->slab_first);
2138     
2139     /* Check whether we have reference data to compare against */
2140     if (erg->slab_last > erg->slab_last_ref)
2141         gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
2142                   RotStr, erg->slab_last);
2143 }
2144
2145
2146 /* Enforced rotation with a flexible axis */
2147 static void do_flexible(
2148         t_commrec *cr,
2149         gmx_enfrot_t enfrot,    /* Other rotation data                        */
2150         t_rotgrp  *rotg,        /* The rotation group                         */
2151         int       g,            /* Group number                               */
2152         rvec      x[],          /* The local positions                        */
2153         matrix    box,
2154         double    t,            /* Time in picoseconds                        */
2155         gmx_large_int_t step,   /* The time step                              */
2156         gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
2157         gmx_bool  bOutstepSlab) /* Output per-slab data                       */
2158 {
2159     int          l,nslabs;
2160     real         sigma;       /* The Gaussian width sigma */
2161     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
2162
2163     
2164     erg=rotg->enfrotgrp;
2165
2166     /* Define the sigma value */
2167     sigma = 0.7*rotg->slab_dist;
2168     
2169     /* Sort the collective coordinates erg->xc along the rotation vector. This is
2170      * an optimization for the inner loop. */
2171     sort_collective_coordinates(rotg, enfrot->data);
2172     
2173     /* Determine the first relevant slab for the first atom and the last
2174      * relevant slab for the last atom */
2175     get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g, cr);
2176     
2177     /* Determine for each slab depending on the min_gaussian cutoff criterium,
2178      * a first and a last atom index inbetween stuff needs to be calculated */
2179     get_firstlast_atom_per_slab(rotg, cr);
2180
2181     /* Determine the gaussian-weighted center of positions for all slabs */
2182     get_slab_centers(rotg,erg->xc,erg->mc_sorted,cr,g,t,enfrot->out_slabs,bOutstepSlab,FALSE);
2183         
2184     /* Clear the torque per slab from last time step: */
2185     nslabs = erg->slab_last - erg->slab_first + 1;
2186     for (l=0; l<nslabs; l++)
2187         erg->slab_torque_v[l] = 0.0;
2188     
2189     /* Call the rotational forces kernel */
2190     GMX_MPE_LOG(ev_flexll_start);
2191     if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
2192         erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, box, cr);
2193     else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
2194         erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, box, cr);
2195     else
2196         gmx_fatal(FARGS, "Unknown flexible rotation type");
2197     GMX_MPE_LOG(ev_flexll_finish);
2198     
2199     /* Determine angle by RMSD fit to the reference - Let's hope this */
2200     /* only happens once in a while, since this is not parallelized! */
2201     if (MASTER(cr))
2202     {
2203         if (bOutstepRot)
2204         {
2205             /* Fit angle of the whole rotation group */
2206             erg->angle_v = flex_fit_angle(rotg);
2207         }
2208         if (bOutstepSlab)
2209         {
2210             /* Fit angle of each slab */
2211             flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
2212         }
2213     }
2214
2215     /* Lump together the torques from all slabs: */
2216     erg->torque_v = 0.0;
2217     for (l=0; l<nslabs; l++)
2218          erg->torque_v += erg->slab_torque_v[l];
2219 }
2220
2221
2222 /* Calculate the angle between reference and actual rotation group atom,
2223  * both projected into a plane perpendicular to the rotation vector: */
2224 static void angle(t_rotgrp *rotg,
2225         rvec x_act,
2226         rvec x_ref,
2227         real *alpha,
2228         real *weight)  /* atoms near the rotation axis should count less than atoms far away */
2229 {
2230     rvec xp, xrp;  /* current and reference positions projected on a plane perpendicular to pg->vec */
2231     rvec dum;
2232
2233
2234     /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
2235     /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
2236     svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
2237     rvec_sub(x_ref, dum, xrp);
2238     /* Project x_act: */
2239     svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
2240     rvec_sub(x_act, dum, xp);
2241
2242     /* Retrieve information about which vector precedes. gmx_angle always
2243      * returns a positive angle. */
2244     cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
2245
2246     if (iprod(rotg->vec, dum) >= 0)
2247         *alpha = -gmx_angle(xrp, xp);
2248     else
2249         *alpha = +gmx_angle(xrp, xp);
2250     
2251     /* Also return the weight */
2252     *weight = norm(xp);
2253 }
2254
2255
2256 /* Project first vector onto a plane perpendicular to the second vector 
2257  * dr = dr - (dr.v)v
2258  * Note that v must be of unit length.
2259  */
2260 static inline void project_onto_plane(rvec dr, const rvec v)
2261 {
2262     rvec tmp;
2263     
2264     
2265     svmul(iprod(dr,v),v,tmp);  /* tmp = (dr.v)v */
2266     rvec_dec(dr, tmp);         /* dr = dr - (dr.v)v */
2267 }
2268
2269
2270 /* Fixed rotation: The rotation reference group rotates around an axis */
2271 /* The atoms of the actual rotation group are attached with imaginary  */
2272 /* springs to the reference atoms.                                     */
2273 static void do_fixed(
2274         t_commrec *cr,
2275         t_rotgrp  *rotg,        /* The rotation group          */
2276         rvec      x[],          /* The positions               */
2277         matrix    box,          /* The simulation box          */
2278         double    t,            /* Time in picoseconds         */
2279         gmx_large_int_t step,   /* The time step               */
2280         gmx_bool  bTorque)
2281 {
2282     int       j,m;
2283     rvec      dr;
2284     rvec      tmp_f;           /* Force */
2285     real      alpha;           /* a single angle between an actual and a reference position */
2286     real      weight;          /* single weight for a single angle */
2287     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2288     rvec      tmpvec;
2289
2290     /* for mass weighting: */
2291     real      wi;              /* Mass-weighting of the positions */
2292     real      N_M;             /* N/M */
2293     real      k_wi;            /* k times wi */
2294
2295     gmx_bool  bProject;
2296
2297     
2298     erg=rotg->enfrotgrp;
2299     bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
2300
2301     N_M = rotg->nat * erg->invmass;
2302
2303     /* Each process calculates the forces on its local atoms */
2304     for (j=0; j<erg->nat_loc; j++)
2305     {
2306         /* Calculate (x_i-x_c) resp. (x_i-u) */
2307         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, tmpvec);
2308
2309         /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2310         rvec_sub(erg->xr_loc[j], tmpvec, dr);
2311         
2312         if (bProject)
2313             project_onto_plane(dr, rotg->vec);
2314             
2315         /* Mass-weighting */
2316         wi = N_M*erg->m_loc[j];
2317
2318         /* Store the additional force so that it can be added to the force
2319          * array after the normal forces have been evaluated */
2320         k_wi = rotg->k*wi;
2321         for (m=0; m<DIM; m++)
2322         {
2323             tmp_f[m]             = k_wi*dr[m];
2324             erg->f_rot_loc[j][m] = tmp_f[m];
2325             erg->V              += 0.5*k_wi*sqr(dr[m]);
2326         }
2327         
2328         if (bTorque)
2329         {
2330             /* Add to the torque of this rotation group */
2331             erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2332             
2333             /* Calculate the angle between reference and actual rotation group atom. */
2334             angle(rotg, tmpvec, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2335             erg->angle_v  += alpha * weight;
2336             erg->weight_v += weight;
2337         }
2338         /* If you want enforced rotation to contribute to the virial,
2339          * activate the following lines:
2340             if (MASTER(cr))
2341             {
2342                Add the rotation contribution to the virial
2343               for(j=0; j<DIM; j++)
2344                 for(m=0;m<DIM;m++)
2345                   vir[j][m] += 0.5*f[ii][j]*dr[m];
2346             }
2347          */
2348
2349         PRINT_FORCE_J
2350
2351     } /* end of loop over local rotation group atoms */
2352 }
2353
2354
2355 /* Calculate the radial motion potential and forces */
2356 static void do_radial_motion(
2357         t_commrec *cr,
2358         t_rotgrp  *rotg,        /* The rotation group          */
2359         rvec      x[],          /* The positions               */
2360         matrix    box,          /* The simulation box          */
2361         double    t,            /* Time in picoseconds         */
2362         gmx_large_int_t step,   /* The time step               */
2363         gmx_bool  bTorque)
2364 {
2365     int       j;
2366     rvec      tmp_f;           /* Force */
2367     real      alpha;           /* a single angle between an actual and a reference position */
2368     real      weight;          /* single weight for a single angle */
2369     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2370     rvec      xj_u;            /* xj - u */
2371     rvec      tmpvec;
2372     real      fac,fac2,sum=0.0;
2373     rvec      pj;
2374
2375     /* For mass weighting: */
2376     real      wj;              /* Mass-weighting of the positions */
2377     real      N_M;             /* N/M */
2378
2379
2380     erg=rotg->enfrotgrp;
2381
2382     N_M = rotg->nat * erg->invmass;
2383
2384     /* Each process calculates the forces on its local atoms */
2385     for (j=0; j<erg->nat_loc; j++)
2386     {
2387         /* Calculate (xj-u) */
2388         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
2389
2390         /* Calculate Omega.(yj-u) */
2391         cprod(rotg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj-u) */
2392
2393                               /*         v x Omega.(yj-u)     */
2394         unitv(tmpvec, pj);    /*  pj = --------------------   */
2395                               /*       | v x Omega.(yj-u) |   */
2396
2397         fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
2398         fac2 = fac*fac;
2399
2400         /* Mass-weighting */
2401         wj = N_M*erg->m_loc[j];
2402
2403         /* Store the additional force so that it can be added to the force
2404          * array after the normal forces have been evaluated */
2405         svmul(-rotg->k*wj*fac, pj, tmp_f);
2406         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2407         sum += wj*fac2;
2408         if (bTorque)
2409         {
2410             /* Add to the torque of this rotation group */
2411             erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2412
2413             /* Calculate the angle between reference and actual rotation group atom. */
2414             angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2415             erg->angle_v  += alpha * weight;
2416             erg->weight_v += weight;
2417         }
2418
2419         PRINT_FORCE_J
2420
2421     } /* end of loop over local rotation group atoms */
2422     erg->V = 0.5*rotg->k*sum;
2423 }
2424
2425
2426 /* Calculate the radial motion pivot-free potential and forces */
2427 static void do_radial_motion_pf(
2428         t_commrec *cr,
2429         t_rotgrp  *rotg,        /* The rotation group          */
2430         rvec      x[],          /* The positions               */
2431         matrix    box,          /* The simulation box          */
2432         double    t,            /* Time in picoseconds         */
2433         gmx_large_int_t step,   /* The time step               */
2434         gmx_bool  bTorque)
2435 {
2436     int       i,ii,iigrp,j;
2437     rvec      xj;              /* Current position */
2438     rvec      xj_xc;           /* xj  - xc  */
2439     rvec      yj0_yc0;         /* yj0 - yc0 */
2440     rvec      tmp_f;           /* Force */
2441     real      alpha;           /* a single angle between an actual and a reference position */
2442     real      weight;          /* single weight for a single angle */
2443     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2444     rvec      tmpvec, tmpvec2;
2445     rvec      innersumvec;     /* Precalculation of the inner sum */
2446     rvec      innersumveckM;
2447     real      fac,fac2,V=0.0;
2448     rvec      qi,qj;
2449
2450     /* For mass weighting: */
2451     real      mj,wi,wj;        /* Mass-weighting of the positions */
2452     real      N_M;             /* N/M */
2453
2454
2455     erg=rotg->enfrotgrp;
2456
2457     N_M = rotg->nat * erg->invmass;
2458
2459     /* Get the current center of the rotation group: */
2460     get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
2461
2462     /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
2463     clear_rvec(innersumvec);
2464     for (i=0; i < rotg->nat; i++)
2465     {
2466         /* Mass-weighting */
2467         wi = N_M*erg->mc[i];
2468
2469         /* Calculate qi. Note that xc_ref_center has already been subtracted from
2470          * x_ref in init_rot_group.*/
2471         mvmul(erg->rotmat, rotg->x_ref[i], tmpvec);  /* tmpvec  = Omega.(yi0-yc0) */
2472
2473         cprod(rotg->vec, tmpvec, tmpvec2);          /* tmpvec2 = v x Omega.(yi0-yc0) */
2474
2475                               /*         v x Omega.(yi0-yc0)     */
2476         unitv(tmpvec2, qi);   /*  qi = -----------------------   */
2477                               /*       | v x Omega.(yi0-yc0) |   */
2478
2479         rvec_sub(erg->xc[i], erg->xc_center, tmpvec);  /* tmpvec = xi-xc */
2480
2481         svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
2482
2483         rvec_inc(innersumvec, tmpvec2);
2484     }
2485     svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
2486
2487     /* Each process calculates the forces on its local atoms */
2488     for (j=0; j<erg->nat_loc; j++)
2489     {
2490         /* Local index of a rotation group atom  */
2491         ii = erg->ind_loc[j];
2492         /* Position of this atom in the collective array */
2493         iigrp = erg->xc_ref_ind[j];
2494         /* Mass-weighting */
2495         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2496         wj = N_M*mj;
2497
2498         /* Current position of this atom: x[ii][XX/YY/ZZ] */
2499         copy_rvec(x[ii], xj);
2500
2501         /* Shift this atom such that it is near its reference */
2502         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2503
2504         /* The (unrotated) reference position is yj0. yc0 has already
2505          * been subtracted in init_rot_group */
2506         copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
2507
2508         /* Calculate Omega.(yj0-yc0) */
2509         mvmul(erg->rotmat, yj0_yc0, tmpvec2);     /* tmpvec2 = Omega.(yj0 - yc0)  */
2510
2511         cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
2512
2513                               /*         v x Omega.(yj0-yc0)     */
2514         unitv(tmpvec, qj);    /*  qj = -----------------------   */
2515                               /*       | v x Omega.(yj0-yc0) |   */
2516
2517         /* Calculate (xj-xc) */
2518         rvec_sub(xj, erg->xc_center, xj_xc);  /* xj_xc = xj-xc */
2519
2520         fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
2521         fac2 = fac*fac;
2522
2523         /* Store the additional force so that it can be added to the force
2524          * array after the normal forces have been evaluated */
2525         svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
2526         svmul(mj, innersumveckM, tmpvec);  /* part 2 of force */
2527         rvec_inc(tmp_f, tmpvec);
2528         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2529         V += wj*fac2;
2530         if (bTorque)
2531         {
2532             /* Add to the torque of this rotation group */
2533             erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
2534
2535             /* Calculate the angle between reference and actual rotation group atom. */
2536             angle(rotg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
2537             erg->angle_v  += alpha * weight;
2538             erg->weight_v += weight;
2539         }
2540
2541         PRINT_FORCE_J
2542
2543     } /* end of loop over local rotation group atoms */
2544     erg->V = 0.5*rotg->k*V;
2545 }
2546
2547
2548 /* Precalculate the inner sum for the radial motion 2 forces */
2549 static void radial_motion2_precalc_inner_sum(t_rotgrp  *rotg, rvec innersumvec)
2550 {
2551     int       i;
2552     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2553     rvec      xi_xc;           /* xj - xc */
2554     rvec      tmpvec,tmpvec2;
2555     real      fac,fac2;
2556     rvec      ri,si;
2557     real      siri;
2558     rvec      v_xi_xc;          /* v x (xj - u) */
2559     real      psii,psiistar;
2560     real      wi;              /* Mass-weighting of the positions */
2561     real      N_M;             /* N/M */
2562     rvec      sumvec;
2563
2564     erg=rotg->enfrotgrp;
2565     N_M = rotg->nat * erg->invmass;
2566
2567     /* Loop over the collective set of positions */
2568     clear_rvec(sumvec);
2569     for (i=0; i<rotg->nat; i++)
2570     {
2571         /* Mass-weighting */
2572         wi = N_M*erg->mc[i];
2573
2574         rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
2575
2576         /* Calculate ri. Note that xc_ref_center has already been subtracted from
2577          * x_ref in init_rot_group.*/
2578         mvmul(erg->rotmat, rotg->x_ref[i], ri);      /* ri  = Omega.(yi0-yc0) */
2579
2580         cprod(rotg->vec, xi_xc, v_xi_xc);            /* v_xi_xc = v x (xi-u)  */
2581
2582         fac = norm2(v_xi_xc);
2583                                           /*                      1           */
2584         psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
2585                                           /*            |v x (xi-xc)|^2 + eps */
2586
2587         psii = gmx_invsqrt(fac);          /*                 1                */
2588                                           /*  psii    = -------------         */
2589                                           /*            |v x (xi-xc)|         */
2590
2591         svmul(psii, v_xi_xc, si);          /*  si = psii * (v x (xi-xc) )     */
2592
2593         fac = iprod(v_xi_xc, ri);                   /* fac = (v x (xi-xc)).ri */
2594         fac2 = fac*fac;
2595
2596         siri = iprod(si, ri);                       /* siri = si.ri           */
2597
2598         svmul(psiistar/psii, ri, tmpvec);
2599         svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
2600         rvec_dec(tmpvec, tmpvec2);
2601         cprod(tmpvec, rotg->vec, tmpvec2);
2602
2603         svmul(wi*siri, tmpvec2, tmpvec);
2604
2605         rvec_inc(sumvec, tmpvec);
2606     }
2607     svmul(rotg->k*erg->invmass, sumvec, innersumvec);
2608 }
2609
2610
2611 /* Calculate the radial motion 2 potential and forces */
2612 static void do_radial_motion2(
2613         t_commrec *cr,
2614         t_rotgrp  *rotg,        /* The rotation group          */
2615         rvec      x[],          /* The positions               */
2616         matrix    box,          /* The simulation box          */
2617         double    t,            /* Time in picoseconds         */
2618         gmx_large_int_t step,   /* The time step               */
2619         gmx_bool  bTorque)
2620 {
2621     int       ii,iigrp,j;
2622     rvec      xj;              /* Position */
2623     real      alpha;           /* a single angle between an actual and a reference position */
2624     real      weight;          /* single weight for a single angle */
2625     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2626     rvec      xj_u;            /* xj - u */
2627     rvec      tmpvec,tmpvec2;
2628     real      fac,fac2,Vpart=0.0;
2629     rvec      rj,sj;
2630     real      sjrj;
2631     rvec      v_xj_u;          /* v x (xj - u) */
2632     real      psij,psijstar;
2633     real      mj,wj;           /* For mass-weighting of the positions */
2634     real      N_M;             /* N/M */
2635     gmx_bool  bPF;
2636     rvec      innersumvec;
2637
2638
2639     erg=rotg->enfrotgrp;
2640
2641     bPF = rotg->eType==erotgRM2PF;
2642     clear_rvec(innersumvec);
2643     if (bPF)
2644     {
2645         /* For the pivot-free variant we have to use the current center of
2646          * mass of the rotation group instead of the pivot u */
2647         get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
2648
2649         /* Also, we precalculate the second term of the forces that is identical
2650          * (up to the weight factor mj) for all forces */
2651         radial_motion2_precalc_inner_sum(rotg,innersumvec);
2652     }
2653
2654     N_M = rotg->nat * erg->invmass;
2655
2656     /* Each process calculates the forces on its local atoms */
2657     for (j=0; j<erg->nat_loc; j++)
2658     {
2659         if (bPF)
2660         {
2661             /* Local index of a rotation group atom  */
2662             ii = erg->ind_loc[j];
2663             /* Position of this atom in the collective array */
2664             iigrp = erg->xc_ref_ind[j];
2665             /* Mass-weighting */
2666             mj = erg->mc[iigrp];
2667
2668             /* Current position of this atom: x[ii] */
2669             copy_rvec(x[ii], xj);
2670
2671             /* Shift this atom such that it is near its reference */
2672             shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2673
2674             /* The (unrotated) reference position is yj0. yc0 has already
2675              * been subtracted in init_rot_group */
2676             copy_rvec(rotg->x_ref[iigrp], tmpvec);     /* tmpvec = yj0 - yc0  */
2677
2678             /* Calculate Omega.(yj0-yc0) */
2679             mvmul(erg->rotmat, tmpvec, rj);          /* rj = Omega.(yj0-yc0)  */
2680         }
2681         else
2682         {
2683             mj = erg->m_loc[j];
2684             copy_rvec(erg->x_loc_pbc[j], xj);
2685             copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
2686         }
2687         /* Mass-weighting */
2688         wj = N_M*mj;
2689
2690         /* Calculate (xj-u) resp. (xj-xc) */
2691         rvec_sub(xj, erg->xc_center, xj_u);          /* xj_u = xj-u           */
2692
2693         cprod(rotg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
2694
2695         fac = norm2(v_xj_u);
2696                                           /*                      1           */
2697         psijstar = 1.0/(fac + rotg->eps); /*  psistar = --------------------  */
2698                                           /*            |v x (xj-u)|^2 + eps  */
2699
2700         psij = gmx_invsqrt(fac);          /*                 1                */
2701                                           /*  psij    = ------------          */
2702                                           /*            |v x (xj-u)|          */
2703
2704         svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
2705
2706         fac = iprod(v_xj_u, rj);                     /* fac = (v x (xj-u)).rj */
2707         fac2 = fac*fac;
2708
2709         sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
2710
2711         svmul(psijstar/psij, rj, tmpvec);
2712         svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
2713         rvec_dec(tmpvec, tmpvec2);
2714         cprod(tmpvec, rotg->vec, tmpvec2);
2715
2716         /* Store the additional force so that it can be added to the force
2717          * array after the normal forces have been evaluated */
2718         svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
2719         svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
2720
2721         rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
2722         Vpart += wj*psijstar*fac2;
2723         if (bTorque)
2724         {
2725             /* Add to the torque of this rotation group */
2726             erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
2727
2728             /* Calculate the angle between reference and actual rotation group atom. */
2729             angle(rotg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
2730             erg->angle_v  += alpha * weight;
2731             erg->weight_v += weight;
2732         }
2733
2734         PRINT_FORCE_J
2735
2736     } /* end of loop over local rotation group atoms */
2737     erg->V = 0.5*rotg->k*Vpart;
2738 }
2739
2740
2741 /* Determine the smallest and largest position vector (with respect to the 
2742  * rotation vector) for the reference group */
2743 static void get_firstlast_atom_ref(
2744         t_rotgrp  *rotg, 
2745         int       *firstindex, 
2746         int       *lastindex)
2747 {
2748     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2749     int i;
2750     real xcproj;               /* The projection of a reference position on the 
2751                                   rotation vector */
2752     real minproj, maxproj;     /* Smallest and largest projection on v */
2753     
2754
2755     
2756     erg=rotg->enfrotgrp;
2757
2758     /* Start with some value */
2759     minproj = iprod(rotg->x_ref[0], rotg->vec);
2760     maxproj = minproj;
2761     
2762     /* This is just to ensure that it still works if all the atoms of the 
2763      * reference structure are situated in a plane perpendicular to the rotation 
2764      * vector */
2765     *firstindex = 0;
2766     *lastindex  = rotg->nat-1;
2767     
2768     /* Loop over all atoms of the reference group, 
2769      * project them on the rotation vector to find the extremes */
2770     for (i=0; i<rotg->nat; i++)
2771     {
2772         xcproj = iprod(rotg->x_ref[i], rotg->vec);
2773         if (xcproj < minproj)
2774         {
2775             minproj = xcproj;
2776             *firstindex = i;
2777         }
2778         if (xcproj > maxproj)
2779         {
2780             maxproj = xcproj;
2781             *lastindex = i;
2782         }
2783     }
2784 }
2785
2786
2787 /* Allocate memory for the slabs */
2788 static void allocate_slabs(
2789         t_rotgrp  *rotg, 
2790         FILE      *fplog, 
2791         int       g, 
2792         gmx_bool  bVerbose,
2793         t_commrec *cr)
2794 {
2795     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
2796     int i, nslabs;
2797     
2798     
2799     erg=rotg->enfrotgrp;
2800     
2801     /* More slabs than are defined for the reference are never needed */
2802     nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
2803     
2804     /* Remember how many we allocated */
2805     erg->nslabs_alloc = nslabs;
2806
2807     if (MASTER(cr) && bVerbose)
2808         fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
2809                 RotStr, nslabs,g);
2810     snew(erg->slab_center     , nslabs);
2811     snew(erg->slab_center_ref , nslabs);
2812     snew(erg->slab_weights    , nslabs);
2813     snew(erg->slab_torque_v   , nslabs);
2814     snew(erg->slab_data       , nslabs);
2815     snew(erg->gn_atom         , nslabs);
2816     snew(erg->gn_slabind      , nslabs);
2817     snew(erg->slab_innersumvec, nslabs);
2818     for (i=0; i<nslabs; i++)
2819     {
2820         snew(erg->slab_data[i].x     , rotg->nat);
2821         snew(erg->slab_data[i].ref   , rotg->nat);
2822         snew(erg->slab_data[i].weight, rotg->nat);
2823     }
2824     snew(erg->xc_ref_sorted, rotg->nat);
2825     snew(erg->xc_sortind   , rotg->nat);
2826     snew(erg->firstatom    , nslabs);
2827     snew(erg->lastatom     , nslabs);
2828 }
2829
2830
2831 /* From the extreme coordinates of the reference group, determine the first 
2832  * and last slab of the reference. We can never have more slabs in the real
2833  * simulation than calculated here for the reference.
2834  */
2835 static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
2836 {
2837     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
2838     int first,last,firststart;
2839     rvec dummy;
2840
2841     
2842     erg=rotg->enfrotgrp;
2843     first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
2844     last  = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
2845     firststart = first;
2846
2847     while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
2848     {
2849         first--;
2850     }
2851     erg->slab_first_ref = first+1;
2852     while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
2853     {
2854         last++;
2855     }
2856     erg->slab_last_ref  = last-1;
2857     
2858     erg->slab_buffer = firststart - erg->slab_first_ref;
2859 }
2860
2861
2862
2863 static void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
2864         rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs)
2865 {
2866     int i,ii;
2867     rvec        coord,*xdum;
2868     gmx_bool    bFlex,bColl;
2869     t_atom      *atom;
2870     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
2871     int         ref_firstindex, ref_lastindex;
2872     real        mass,totalmass;
2873     
2874
2875     /* Do we have a flexible axis? */
2876     bFlex = ISFLEX(rotg);
2877     /* Do we use a global set of coordinates? */
2878     bColl = ISCOLL(rotg);
2879
2880     erg=rotg->enfrotgrp;
2881     
2882     /* Allocate space for collective coordinates if needed */
2883     if (bColl)
2884     {
2885         snew(erg->xc        , rotg->nat);
2886         snew(erg->xc_shifts , rotg->nat);
2887         snew(erg->xc_eshifts, rotg->nat);
2888
2889         /* Save the original (whole) set of positions such that later the
2890          * molecule can always be made whole again */
2891         snew(erg->xc_old    , rotg->nat);        
2892         if (MASTER(cr))
2893         {
2894             for (i=0; i<rotg->nat; i++)
2895             {
2896                 ii = rotg->ind[i];
2897                 copy_rvec(x[ii], erg->xc_old[i]);
2898             }
2899         }
2900 #ifdef GMX_MPI
2901         if (PAR(cr))
2902             gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
2903 #endif
2904
2905         if (rotg->eFittype == erotgFitNORM)
2906         {
2907             snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
2908             snew(erg->xc_norm      , rotg->nat);
2909         }
2910     }
2911     else
2912     {
2913         snew(erg->xr_loc   , rotg->nat);
2914         snew(erg->x_loc_pbc, rotg->nat);
2915     }
2916     
2917     snew(erg->f_rot_loc , rotg->nat);
2918     snew(erg->xc_ref_ind, rotg->nat);
2919     
2920     /* xc_ref_ind needs to be set to identity in the serial case */
2921     if (!PAR(cr))
2922         for (i=0; i<rotg->nat; i++)
2923             erg->xc_ref_ind[i] = i;
2924
2925     /* Copy the masses so that the COM can be determined. For all types of
2926      * enforced rotation, we store the masses in the erg->mc array. */
2927     snew(erg->mc, rotg->nat);
2928     if (bFlex)
2929         snew(erg->mc_sorted, rotg->nat);
2930     if (!bColl)
2931         snew(erg->m_loc, rotg->nat);
2932     totalmass=0.0;
2933     for (i=0; i<rotg->nat; i++)
2934     {
2935         if (rotg->bMassW)
2936         {
2937             gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
2938             mass=atom->m;
2939         }
2940         else
2941         {
2942             mass=1.0;
2943         }
2944         erg->mc[i] = mass;
2945         totalmass += mass;
2946     }
2947     erg->invmass = 1.0/totalmass;
2948     
2949     /* Set xc_ref_center for any rotation potential */
2950     if ((rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2))
2951     {
2952         /* Set the pivot point for the fixed, stationary-axis potentials. This
2953          * won't change during the simulation */
2954         copy_rvec(rotg->pivot, erg->xc_ref_center);
2955         copy_rvec(rotg->pivot, erg->xc_center    );
2956     }
2957     else
2958     {
2959         /* Center of the reference positions */
2960         get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
2961
2962         /* Center of the actual positions */
2963         if (MASTER(cr))
2964         {
2965             snew(xdum, rotg->nat);
2966             for (i=0; i<rotg->nat; i++)
2967             {
2968                 ii = rotg->ind[i];
2969                 copy_rvec(x[ii], xdum[i]);
2970             }
2971             get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
2972             sfree(xdum);
2973         }
2974 #ifdef GMX_MPI
2975         if (PAR(cr))
2976             gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
2977 #endif
2978     }
2979
2980     if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
2981     {
2982         /* Put the reference positions into origin: */
2983         for (i=0; i<rotg->nat; i++)
2984             rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
2985     }
2986
2987     /* Enforced rotation with flexible axis */
2988     if (bFlex)
2989     {
2990         /* Calculate maximum beta value from minimum gaussian (performance opt.) */
2991         erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
2992
2993         /* Determine the smallest and largest coordinate with respect to the rotation vector */
2994         get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
2995         
2996         /* From the extreme coordinates of the reference group, determine the first 
2997          * and last slab of the reference. */
2998         get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
2999                 
3000         /* Allocate memory for the slabs */
3001         allocate_slabs(rotg, fplog, g, bVerbose, cr);
3002
3003         /* Flexible rotation: determine the reference centers for the rest of the simulation */
3004         erg->slab_first = erg->slab_first_ref;
3005         erg->slab_last = erg->slab_last_ref;
3006         get_slab_centers(rotg,rotg->x_ref,erg->mc,cr,g,-1,out_slabs,TRUE,TRUE);
3007
3008         /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
3009         if (rotg->eFittype == erotgFitNORM)
3010         {
3011             for (i=0; i<rotg->nat; i++)
3012             {
3013                 rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
3014                 erg->xc_ref_length[i] = norm(coord);
3015             }
3016         }
3017     }
3018 }
3019
3020
3021 extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot)
3022 {
3023     gmx_ga2la_t ga2la;
3024     int g;
3025     t_rotgrp *rotg;
3026     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
3027     
3028     ga2la = dd->ga2la;
3029
3030     for(g=0; g<rot->ngrp; g++)
3031     {
3032         rotg = &rot->grp[g];
3033         erg  = rotg->enfrotgrp;
3034
3035
3036         dd_make_local_group_indices(ga2la,rotg->nat,rotg->ind,
3037                 &erg->nat_loc,&erg->ind_loc,&erg->nalloc_loc,erg->xc_ref_ind);
3038     }
3039 }
3040
3041
3042 extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
3043         t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
3044         gmx_bool bVerbose, unsigned long Flags)
3045 {
3046     t_rot    *rot;
3047     t_rotgrp *rotg;
3048     int      g;
3049     int      nat_max=0;     /* Size of biggest rotation group */
3050     gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
3051     gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
3052     rvec     *x_pbc=NULL;   /* Space for the pbc-correct atom positions */
3053     gmx_bool bHaveFlexGroups = FALSE;
3054
3055
3056     if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
3057         gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
3058
3059     if ( MASTER(cr) && bVerbose)
3060         fprintf(stdout, "%s Initializing ...\n", RotStr);
3061
3062
3063     rot = ir->rot;
3064     snew(rot->enfrot, 1);
3065     er=rot->enfrot;
3066     
3067     /* Output every step for reruns */
3068     if (Flags & MD_RERUN)
3069     {
3070         if (fplog)
3071             fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
3072         rot->nstrout = 1;
3073         rot->nstsout = 1;
3074     }
3075
3076     er->out_slabs = NULL;
3077     if (MASTER(cr))
3078         er->out_slabs = open_slab_out(rot, opt2fn("-rs",nfile,fnm));
3079
3080     if (MASTER(cr))
3081     {
3082         /* Remove pbc, make molecule whole.
3083          * When ir->bContinuation=TRUE this has already been done, but ok. */
3084         snew(x_pbc,mtop->natoms);
3085         m_rveccopy(mtop->natoms,x,x_pbc);
3086         do_pbc_first_mtop(NULL,ir->ePBC,box,mtop,x_pbc);
3087     }
3088
3089     for (g=0; g<rot->ngrp; g++)
3090     {
3091         rotg = &rot->grp[g];
3092         if (ISFLEX(rotg))
3093             bHaveFlexGroups = TRUE;
3094
3095         if (fplog)
3096             fprintf(fplog,"%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
3097         
3098         if (rotg->nat > 0)
3099         {
3100             /* Allocate space for the rotation group's data: */
3101             snew(rotg->enfrotgrp, 1);
3102             erg  = rotg->enfrotgrp;
3103
3104             nat_max=max(nat_max, rotg->nat);
3105             
3106             if (PAR(cr))
3107             {
3108                 erg->nat_loc    = 0;
3109                 erg->nalloc_loc = 0;
3110                 erg->ind_loc    = NULL;
3111             }
3112             else
3113             {
3114                 erg->nat_loc = rotg->nat;
3115                 erg->ind_loc = rotg->ind;
3116             }
3117             init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs);
3118         }
3119     }
3120     
3121     /* Allocate space for enforced rotation buffer variables */
3122     er->bufsize = nat_max;
3123     snew(er->data, nat_max);
3124     snew(er->xbuf, nat_max);
3125     snew(er->mbuf, nat_max);
3126
3127     /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
3128     er->mpi_bufsize = 4*rot->ngrp; /* To start with */
3129     snew(er->mpi_inbuf , er->mpi_bufsize);
3130     snew(er->mpi_outbuf, er->mpi_bufsize);
3131
3132     /* Only do I/O on the MASTER */
3133     er->out_angles  = NULL;
3134     er->out_rot     = NULL;
3135     er->out_torque  = NULL;
3136     if (MASTER(cr))
3137     {
3138         er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv, Flags);
3139         if (bHaveFlexGroups)
3140         {
3141             if (rot->nstrout > 0)
3142                 er->out_angles  = open_angles_out(rot, opt2fn("-ra",nfile,fnm));
3143             if (rot->nstsout > 0)
3144                 er->out_torque  = open_torque_out(rot, opt2fn("-rt",nfile,fnm));
3145         }
3146         sfree(x_pbc);
3147     }
3148 }
3149
3150
3151 extern void finish_rot(FILE *fplog,t_rot *rot)
3152 {
3153     gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
3154
3155     
3156     er=rot->enfrot;
3157     if (er->out_rot)
3158         gmx_fio_fclose(er->out_rot);
3159     if (er->out_slabs)
3160         gmx_fio_fclose(er->out_slabs);
3161     if (er->out_angles)
3162         gmx_fio_fclose(er->out_angles);
3163     if (er->out_torque)
3164         gmx_fio_fclose(er->out_torque);
3165 }
3166
3167
3168 /* Rotate the local reference positions and store them in
3169  * erg->xr_loc[0...(nat_loc-1)]
3170  *
3171  * Note that we already subtracted u or y_c from the reference positions
3172  * in init_rot_group().
3173  */
3174 static void rotate_local_reference(t_rotgrp *rotg)
3175 {
3176     gmx_enfrotgrp_t erg;
3177     int i,ii;
3178
3179     
3180     erg=rotg->enfrotgrp;
3181     
3182     for (i=0; i<erg->nat_loc; i++)
3183     {
3184         /* Index of this rotation group atom with respect to the whole rotation group */
3185         ii = erg->xc_ref_ind[i];
3186         /* Rotate */
3187         mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
3188     }
3189 }
3190
3191
3192 /* Select the PBC representation for each local x position and store that
3193  * for later usage. We assume the right PBC image of an x is the one nearest to
3194  * its rotated reference */
3195 static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
3196 {
3197     int d,i,ii,m;
3198     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
3199     rvec xref,xcurr,dx;
3200     ivec shift;
3201
3202
3203     erg=rotg->enfrotgrp;
3204     
3205     for (i=0; i<erg->nat_loc; i++)
3206     {
3207         clear_ivec(shift);
3208         
3209         /* Index of a rotation group atom  */
3210         ii = erg->ind_loc[i];
3211
3212         /* Get the reference position. The pivot (or COM or COG) was already
3213          * subtracted in init_rot_group() from the reference positions. Also,
3214          * the reference positions have already been rotated in
3215          * rotate_local_reference() */
3216         copy_rvec(erg->xr_loc[i], xref);
3217         
3218         /* Subtract the (old) center from the current positions
3219          * (just to determine the shifts!) */
3220         rvec_sub(x[ii], erg->xc_center, xcurr);
3221         
3222         /* Shortest PBC distance between the atom and its reference */
3223         rvec_sub(xcurr, xref, dx);
3224         
3225         /* Determine the shift for this atom */
3226         for(m=npbcdim-1; m>=0; m--)
3227         {
3228             while (dx[m] < -0.5*box[m][m])
3229             {
3230                 for(d=0; d<DIM; d++)
3231                     dx[d] += box[m][d];
3232                 shift[m]++;
3233             }
3234             while (dx[m] >= 0.5*box[m][m])
3235             {
3236                 for(d=0; d<DIM; d++)
3237                     dx[d] -= box[m][d];
3238                 shift[m]--;
3239             }
3240         }
3241         
3242         /* Apply the shift to the current atom */
3243         copy_rvec(x[ii], erg->x_loc_pbc[i]);
3244         shift_single_coord(box, erg->x_loc_pbc[i], shift); 
3245     }
3246 }
3247
3248
3249 extern void do_rotation(
3250         t_commrec *cr,
3251         t_inputrec *ir,
3252         matrix box,
3253         rvec x[],
3254         real t,
3255         gmx_large_int_t step,
3256         gmx_wallcycle_t wcycle,
3257         gmx_bool bNS)
3258 {
3259     int      g,i,ii;
3260     t_rot    *rot;
3261     t_rotgrp *rotg;
3262     gmx_bool outstep_slab, outstep_rot;
3263     gmx_bool bFlex,bColl;
3264     float    cycles_rot;
3265     gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
3266     gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
3267     rvec     transvec;
3268 #ifdef TAKETIME
3269     double t0;
3270 #endif
3271     
3272     
3273     rot=ir->rot;
3274     er=rot->enfrot;
3275     
3276     /* When to output in main rotation output file */
3277     outstep_rot  = do_per_step(step, rot->nstrout);
3278     /* When to output per-slab data */
3279     outstep_slab = do_per_step(step, rot->nstsout);
3280
3281     /* Output time into rotation output file */
3282     if (outstep_rot && MASTER(cr))
3283         fprintf(er->out_rot, "%12.3e",t);
3284
3285     /**************************************************************************/
3286     /* First do ALL the communication! */
3287     for(g=0; g<rot->ngrp; g++)
3288     {
3289         rotg = &rot->grp[g];
3290         erg=rotg->enfrotgrp;
3291
3292         /* Do we have a flexible axis? */
3293         bFlex = ISFLEX(rotg);
3294         /* Do we use a collective (global) set of coordinates? */
3295         bColl = ISCOLL(rotg);
3296
3297         /* Calculate the rotation matrix for this angle: */
3298         erg->degangle = rotg->rate * t;
3299         calc_rotmat(rotg->vec,erg->degangle,erg->rotmat);
3300
3301         if (bColl)
3302         {
3303             /* Transfer the rotation group's positions such that every node has
3304              * all of them. Every node contributes its local positions x and stores
3305              * it in the collective erg->xc array. */
3306             communicate_group_positions(cr,erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
3307                     x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
3308         }
3309         else
3310         {
3311             /* Fill the local masses array;
3312              * this array changes in DD/neighborsearching steps */
3313             if (bNS)
3314             {
3315                 for (i=0; i<erg->nat_loc; i++)
3316                 {
3317                     /* Index of local atom w.r.t. the collective rotation group */
3318                     ii = erg->xc_ref_ind[i];
3319                     erg->m_loc[i] = erg->mc[ii];
3320                 }
3321             }
3322
3323             /* Calculate Omega*(y_i-y_c) for the local positions */
3324             rotate_local_reference(rotg);
3325
3326             /* Choose the nearest PBC images of the group atoms with respect
3327              * to the rotated reference positions */
3328             choose_pbc_image(x, rotg, box, 3);
3329
3330             /* Get the center of the rotation group */
3331             if ( (rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) )
3332                 get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
3333         }
3334
3335     } /* End of loop over rotation groups */
3336
3337     /**************************************************************************/
3338     /* Done communicating, we can start to count cycles now ... */
3339     wallcycle_start(wcycle, ewcROT);
3340     GMX_MPE_LOG(ev_rotcycles_start);
3341
3342 #ifdef TAKETIME
3343     t0 = MPI_Wtime();
3344 #endif
3345
3346     for(g=0; g<rot->ngrp; g++)
3347     {
3348         rotg = &rot->grp[g];
3349         erg=rotg->enfrotgrp;
3350
3351         bFlex = ISFLEX(rotg);
3352         bColl = ISCOLL(rotg);
3353
3354         if (outstep_rot && MASTER(cr))
3355             fprintf(er->out_rot, "%12.4f", erg->degangle);
3356
3357         /* Clear values from last time step */
3358         erg->V        = 0.0;
3359         erg->torque_v = 0.0;
3360         erg->angle_v  = 0.0;
3361         erg->weight_v = 0.0;
3362
3363         switch(rotg->eType)
3364         {
3365             case erotgISO:
3366             case erotgISOPF:
3367             case erotgPM:
3368             case erotgPMPF:
3369                 do_fixed(cr,rotg,x,box,t,step,outstep_rot);
3370                 break;
3371             case erotgRM:
3372                 do_radial_motion(cr,rotg,x,box,t,step,outstep_rot);
3373                 break;
3374             case erotgRMPF:
3375                 do_radial_motion_pf(cr,rotg,x,box,t,step,outstep_rot);
3376                 break;
3377             case erotgRM2:
3378             case erotgRM2PF:
3379                 do_radial_motion2(cr,rotg,x,box,t,step,outstep_rot);
3380                 break;
3381             case erotgFLEXT:
3382             case erotgFLEX2T:
3383                 /* Subtract the center of the rotation group from the collective positions array
3384                  * Also store the center in erg->xc_center since it needs to be subtracted
3385                  * in the low level routines from the local coordinates as well */
3386                 get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
3387                 svmul(-1.0, erg->xc_center, transvec);
3388                 translate_x(erg->xc, rotg->nat, transvec);
3389                 do_flexible(cr,er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
3390                 break;
3391             case erotgFLEX:
3392             case erotgFLEX2:
3393                 /* Do NOT subtract the center of mass in the low level routines! */
3394                 clear_rvec(erg->xc_center);
3395                 do_flexible(cr,er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
3396                 break;
3397             default:
3398                 gmx_fatal(FARGS, "No such rotation potential.");
3399                 break;
3400         }
3401     }
3402
3403 #ifdef TAKETIME
3404     if (MASTER(cr))
3405         fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
3406 #endif
3407
3408     /* Stop the cycle counter and add to the force cycles for load balancing */
3409     cycles_rot = wallcycle_stop(wcycle,ewcROT);
3410     if (DOMAINDECOMP(cr) && wcycle)
3411         dd_cycles_add(cr->dd,cycles_rot,ddCyclF);
3412     GMX_MPE_LOG(ev_rotcycles_finish);
3413 }