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