Update copyright statements and change license to LGPL
[alexxy/gromacs.git] / src / mdlib / pull_rotation.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2008, The GROMACS development team,
6  * check out http://www.gromacs.org for more information.
7  * Copyright (c) 2012, by the GROMACS development team, led by
8  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9  * others, as listed in the AUTHORS file in the top-level source
10  * directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include "domdec.h"
46 #include "gmx_wallcycle.h"
47 #include "gmx_cyclecounter.h"
48 #include "trnio.h"
49 #include "smalloc.h"
50 #include "network.h"
51 #include "pbc.h"
52 #include "futil.h"
53 #include "mdrun.h"
54 #include "txtdump.h"
55 #include "names.h"
56 #include "mtop_util.h"
57 #include "names.h"
58 #include "nrjac.h"
59 #include "vec.h"
60 #include "gmx_ga2la.h"
61 #include "xvgr.h"
62 #include "gmxfio.h"
63 #include "groupcoord.h"
64 #include "pull_rotation.h"
65 #include "gmx_sort.h"
66 #include "copyrite.h"
67
68
69 static char *RotStr = {"Enforced rotation:"};
70
71
72 /* Set the minimum weight for the determination of the slab centers */
73 #define WEIGHT_MIN (10*GMX_FLOAT_MIN)
74
75 /* Helper structure for sorting positions along rotation vector             */
76 typedef struct {
77     real xcproj;            /* Projection of xc on the rotation vector        */
78     int ind;                /* Index of xc                                    */
79     real m;                 /* Mass                                           */
80     rvec x;                 /* Position                                       */
81     rvec x_ref;             /* Reference position                             */
82 } sort_along_vec_t;
83
84
85 /* Enforced rotation / flexible: determine the angle of each slab             */
86 typedef struct gmx_slabdata
87 {
88     int  nat;               /* Number of atoms belonging to this slab         */
89     rvec *x;                /* The positions belonging to this slab. In 
90                                general, this should be all positions of the 
91                                whole rotation group, but we leave those away 
92                                that have a small enough weight                */
93     rvec *ref;              /* Same for reference                             */
94     real *weight;           /* The weight for each atom                       */
95 } t_gmx_slabdata;
96
97
98 /* Helper structure for potential fitting */
99 typedef struct gmx_potfit
100 {
101     real   *degangle;       /* Set of angles for which the potential is
102                                calculated. The optimum fit is determined as
103                                the angle for with the potential is minimal    */
104     real   *V;              /* Potential for the different angles             */
105     matrix *rotmat;         /* Rotation matrix corresponding to the angles    */
106 } t_gmx_potfit;
107
108
109 /* Enforced rotation data for all groups                                      */
110 typedef struct gmx_enfrot
111 {
112     FILE  *out_rot;         /* Output file for rotation data                  */
113     FILE  *out_torque;      /* Output file for torque data                    */
114     FILE  *out_angles;      /* Output file for slab angles for flexible type  */
115     FILE  *out_slabs;       /* Output file for slab centers                   */
116     int   bufsize;          /* Allocation size of buf                         */
117     rvec  *xbuf;            /* Coordinate buffer variable for sorting         */
118     real  *mbuf;            /* Masses buffer variable for sorting             */
119     sort_along_vec_t *data; /* Buffer variable needed for position sorting    */
120     real  *mpi_inbuf;       /* MPI buffer                                     */
121     real  *mpi_outbuf;      /* MPI buffer                                     */
122     int   mpi_bufsize;      /* Allocation size of in & outbuf                 */
123     unsigned long Flags;    /* mdrun flags                                    */
124     gmx_bool bOut;          /* Used to skip first output when appending to 
125                              * avoid duplicate entries in rotation outfiles   */
126 } t_gmx_enfrot;
127
128
129 /* Global enforced rotation data for a single rotation group                  */
130 typedef struct gmx_enfrotgrp
131 {
132     real    degangle;       /* Rotation angle in degrees                      */
133     matrix  rotmat;         /* Rotation matrix                                */
134     atom_id *ind_loc;       /* Local rotation indices                         */
135     int     nat_loc;        /* Number of local group atoms                    */
136     int     nalloc_loc;     /* Allocation size for ind_loc and weight_loc     */
137
138     real  V;                /* Rotation potential for this rotation group     */
139     rvec  *f_rot_loc;       /* Array to store the forces on the local atoms
140                                resulting from enforced rotation potential     */
141
142     /* Collective coordinates for the whole rotation group */
143     real  *xc_ref_length;   /* Length of each x_rotref vector after x_rotref 
144                                has been put into origin                       */
145     int   *xc_ref_ind;      /* Position of each local atom in the collective
146                                array                                          */
147     rvec  xc_center;        /* Center of the rotation group positions, may
148                                be mass weighted                               */
149     rvec  xc_ref_center;    /* dito, for the reference positions              */
150     rvec  *xc;              /* Current (collective) positions                 */
151     ivec  *xc_shifts;       /* Current (collective) shifts                    */
152     ivec  *xc_eshifts;      /* Extra shifts since last DD step                */
153     rvec  *xc_old;          /* Old (collective) positions                     */
154     rvec  *xc_norm;         /* Normalized form of the current positions       */
155     rvec  *xc_ref_sorted;   /* Reference positions (sorted in the same order 
156                                as xc when sorted)                             */
157     int   *xc_sortind;      /* Where is a position found after sorting?       */
158     real  *mc;              /* Collective masses                              */
159     real  *mc_sorted;
160     real  invmass;          /* one over the total mass of the rotation group  */
161
162     real  torque_v;         /* Torque in the direction of rotation vector     */
163     real  angle_v;          /* Actual angle of the whole rotation group       */
164     /* Fixed rotation only */
165     real  weight_v;         /* Weights for angle determination                */
166     rvec  *xr_loc;          /* Local reference coords, correctly rotated      */
167     rvec  *x_loc_pbc;       /* Local current coords, correct PBC image        */
168     real  *m_loc;           /* Masses of the current local atoms              */
169
170     /* Flexible rotation only */
171     int   nslabs_alloc;     /* For this many slabs memory is allocated        */
172     int   slab_first;       /* Lowermost slab for that the calculation needs 
173                                to be performed at a given time step           */
174     int   slab_last;        /* Uppermost slab ...                             */
175     int   slab_first_ref;   /* First slab for which ref. center is stored     */
176     int   slab_last_ref;    /* Last ...                                       */
177     int   slab_buffer;      /* Slab buffer region around reference slabs      */
178     int   *firstatom;       /* First relevant atom for a slab                 */
179     int   *lastatom;        /* Last relevant atom for a slab                  */
180     rvec  *slab_center;     /* Gaussian-weighted slab center                  */
181     rvec  *slab_center_ref; /* Gaussian-weighted slab center for the
182                                reference positions                            */
183     real  *slab_weights;    /* Sum of gaussian weights in a slab              */
184     real  *slab_torque_v;   /* Torque T = r x f for each slab.                */
185                             /* torque_v = m.v = angular momentum in the 
186                                direction of v                                 */
187     real  max_beta;         /* min_gaussian from inputrec->rotgrp is the
188                                minimum value the gaussian must have so that 
189                                the force is actually evaluated max_beta is 
190                                just another way to put it                     */
191     real  *gn_atom;         /* Precalculated gaussians for a single atom      */
192     int   *gn_slabind;      /* Tells to which slab each precalculated gaussian 
193                                belongs                                        */
194     rvec  *slab_innersumvec;/* Inner sum of the flexible2 potential per slab;
195                                this is precalculated for optimization reasons */
196     t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights 
197                                of atoms belonging to a slab                   */
198
199     /* For potential fits with varying angle: */
200     t_gmx_potfit *PotAngleFit;  /* Used for fit type 'potential'              */
201 } t_gmx_enfrotgrp;
202
203
204 /* Activate output of forces for correctness checks */
205 /* #define PRINT_FORCES */
206 #ifdef PRINT_FORCES
207 #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]);
208 #define PRINT_POT_TAU  if (MASTER(cr)) { \
209                            fprintf(stderr,"potential = %15.8f\n" "torque    = %15.8f\n", erg->V, erg->torque_v); \
210                        }
211 #else
212 #define PRINT_FORCE_J
213 #define PRINT_POT_TAU
214 #endif
215
216 /* Shortcuts for often used queries */
217 #define ISFLEX(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) )
218 #define ISCOLL(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) || (rg->eType==erotgRMPF) || (rg->eType==erotgRM2PF) )
219
220
221 /* Does any of the rotation groups use slab decomposition? */
222 static gmx_bool HaveFlexibleGroups(t_rot *rot)
223 {
224     int g;
225     t_rotgrp *rotg;
226
227
228     for (g=0; g<rot->ngrp; g++)
229     {
230         rotg = &rot->grp[g];
231         if (ISFLEX(rotg))
232             return TRUE;
233     }
234
235     return FALSE;
236 }
237
238
239 /* Is for any group the fit angle determined by finding the minimum of the
240  * rotation potential? */
241 static gmx_bool HavePotFitGroups(t_rot *rot)
242 {
243     int g;
244     t_rotgrp *rotg;
245
246
247     for (g=0; g<rot->ngrp; g++)
248     {
249         rotg = &rot->grp[g];
250         if (erotgFitPOT == rotg->eFittype)
251             return TRUE;
252     }
253
254     return FALSE;
255 }
256
257
258 static double** allocate_square_matrix(int dim)
259 {
260     int i;
261     double** mat = NULL; 
262     
263     
264     snew(mat, dim);
265     for(i=0; i<dim; i++)
266         snew(mat[i], dim);
267
268     return mat;
269 }
270
271
272 static void free_square_matrix(double** mat, int dim)
273 {
274     int i;
275     
276     
277     for (i=0; i<dim; i++)
278         sfree(mat[i]);
279     sfree(mat);
280 }
281
282
283 /* Return the angle for which the potential is minimal */
284 static real get_fitangle(t_rotgrp *rotg, gmx_enfrotgrp_t erg)
285 {
286     int i;
287     real fitangle = -999.9;
288     real pot_min = GMX_FLOAT_MAX;
289     t_gmx_potfit *fit;
290
291
292     fit = erg->PotAngleFit;
293
294     for (i = 0; i < rotg->PotAngle_nstep; i++)
295     {
296         if (fit->V[i] < pot_min)
297         {
298             pot_min = fit->V[i];
299             fitangle = fit->degangle[i];
300         }
301     }
302
303     return fitangle;
304 }
305
306
307 /* Reduce potential angle fit data for this group at this time step? */
308 static gmx_inline gmx_bool bPotAngle(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
309 {
310     return ( (erotgFitPOT==rotg->eFittype) && (do_per_step(step, rot->nstsout) || do_per_step(step, rot->nstrout)) );
311 }
312
313 /* Reduce slab torqe data for this group at this time step? */
314 static gmx_inline gmx_bool bSlabTau(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
315 {
316     return ( (ISFLEX(rotg)) && do_per_step(step, rot->nstsout) );
317 }
318
319 /* Output rotation energy, torques, etc. for each rotation group */
320 static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
321 {
322     int      g,i,islab,nslabs=0;
323     int      count;      /* MPI element counter                               */
324     t_rotgrp *rotg;
325     gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
326     gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
327     real     fitangle;
328     gmx_bool bFlex;
329
330     
331     er=rot->enfrot;
332     
333     /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
334      * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
335     if (PAR(cr))
336     {
337         count=0;
338         for (g=0; g < rot->ngrp; g++)
339         {
340             rotg = &rot->grp[g];
341             erg = rotg->enfrotgrp;
342             nslabs = erg->slab_last - erg->slab_first + 1;
343             er->mpi_inbuf[count++] = erg->V;
344             er->mpi_inbuf[count++] = erg->torque_v;
345             er->mpi_inbuf[count++] = erg->angle_v;
346             er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
347
348             if (bPotAngle(rot, rotg, step))
349             {
350                 for (i = 0; i < rotg->PotAngle_nstep; i++)
351                     er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
352             }
353             if (bSlabTau(rot, rotg, step))
354             {
355                 for (i=0; i<nslabs; i++)
356                     er->mpi_inbuf[count++] = erg->slab_torque_v[i];
357             }
358         }
359         if (count > er->mpi_bufsize)
360             gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
361
362 #ifdef GMX_MPI
363         MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
364 #endif
365
366         /* Copy back the reduced data from the buffer on the master */
367         if (MASTER(cr))
368         {
369             count=0;
370             for (g=0; g < rot->ngrp; g++)
371             {
372                 rotg = &rot->grp[g];
373                 erg = rotg->enfrotgrp;
374                 nslabs = erg->slab_last - erg->slab_first + 1;
375                 erg->V        = er->mpi_outbuf[count++];
376                 erg->torque_v = er->mpi_outbuf[count++];
377                 erg->angle_v  = er->mpi_outbuf[count++];
378                 erg->weight_v = er->mpi_outbuf[count++];
379
380                 if (bPotAngle(rot, rotg, step))
381                 {
382                     for (i = 0; i < rotg->PotAngle_nstep; i++)
383                         erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
384                 }
385                 if (bSlabTau(rot, rotg, step))
386                 {
387                     for (i=0; i<nslabs; i++)
388                         erg->slab_torque_v[i] = er->mpi_outbuf[count++];
389                 }
390             }
391         }
392     }
393     
394     /* Output */
395     if (MASTER(cr))
396     {
397         /* Angle and torque for each rotation group */
398         for (g=0; g < rot->ngrp; g++)
399         {
400             rotg=&rot->grp[g];
401             bFlex = ISFLEX(rotg);
402
403             erg=rotg->enfrotgrp;
404             
405             /* Output to main rotation output file: */
406             if ( do_per_step(step, rot->nstrout) )
407             {
408                 if (erotgFitPOT == rotg->eFittype)
409                 {
410                     fitangle = get_fitangle(rotg, erg);
411                 }
412                 else
413                 {
414                     if (bFlex)
415                         fitangle = erg->angle_v; /* RMSD fit angle */
416                     else
417                         fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
418                 }
419                 fprintf(er->out_rot, "%12.4f", fitangle);
420                 fprintf(er->out_rot, "%12.3e", erg->torque_v);
421                 fprintf(er->out_rot, "%12.3e", erg->V);
422             }
423
424             if ( do_per_step(step, rot->nstsout) )
425             {
426                 /* Output to torque log file: */
427                 if (bFlex)
428                 {
429                     fprintf(er->out_torque, "%12.3e%6d", t, g);
430                     for (i=erg->slab_first; i<=erg->slab_last; i++)
431                     {
432                         islab = i - erg->slab_first;  /* slab index */
433                         /* Only output if enough weight is in slab */
434                         if (erg->slab_weights[islab] > rotg->min_gaussian)
435                             fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
436                     }
437                     fprintf(er->out_torque , "\n");
438                 }
439
440                 /* Output to angles log file: */
441                 if (erotgFitPOT == rotg->eFittype)
442                 {
443                     fprintf(er->out_angles, "%12.3e%6d%12.4f", t, g, erg->degangle);
444                     /* Output energies at a set of angles around the reference angle */
445                     for (i = 0; i < rotg->PotAngle_nstep; i++)
446                         fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
447                     fprintf(er->out_angles, "\n");
448                 }
449             }
450         }
451         if ( do_per_step(step, rot->nstrout) )
452             fprintf(er->out_rot, "\n");
453     }
454 }
455
456
457 /* Add the forces from enforced rotation potential to the local forces.
458  * Should be called after the SR forces have been evaluated */
459 extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
460 {
461     int g,l,ii;
462     t_rotgrp *rotg;
463     gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
464     gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
465     real Vrot = 0.0;     /* If more than one rotation group is present, Vrot
466                             assembles the local parts from all groups         */
467
468     
469     er=rot->enfrot;
470     
471     /* Loop over enforced rotation groups (usually 1, though)
472      * Apply the forces from rotation potentials */
473     for (g=0; g<rot->ngrp; g++)
474     {
475         rotg = &rot->grp[g];
476         erg=rotg->enfrotgrp;
477         Vrot += erg->V;  /* add the local parts from the nodes */
478         for (l=0; l<erg->nat_loc; l++)
479         {
480             /* Get the right index of the local force */
481             ii = erg->ind_loc[l];
482             /* Add */
483             rvec_inc(f[ii],erg->f_rot_loc[l]);
484         }
485     }
486
487     /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
488      * on the master and output these values to file. */
489     if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
490         reduce_output(cr, rot, t, step);
491
492     /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
493     er->bOut = TRUE;
494     
495     PRINT_POT_TAU
496
497     return Vrot;
498 }
499
500
501 /* The Gaussian norm is chosen such that the sum of the gaussian functions
502  * over the slabs is approximately 1.0 everywhere */
503 #define GAUSS_NORM   0.569917543430618
504
505
506 /* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
507  * also does some checks
508  */
509 static double calc_beta_max(real min_gaussian, real slab_dist)
510 {
511     double sigma;
512     double arg;
513     
514     
515     /* Actually the next two checks are already made in grompp */
516     if (slab_dist <= 0)
517         gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
518     if (min_gaussian <= 0)
519         gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
520
521     /* Define the sigma value */
522     sigma = 0.7*slab_dist;
523
524     /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
525     arg = min_gaussian/GAUSS_NORM;
526     if (arg > 1.0)
527         gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
528     
529     return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
530 }
531
532
533 static gmx_inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
534 {
535     return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
536 }
537
538
539 static gmx_inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
540 {
541     const real norm = GAUSS_NORM;
542     real       sigma;
543
544     
545     /* Define the sigma value */
546     sigma = 0.7*rotg->slab_dist;
547     /* Calculate the Gaussian value of slab n for position curr_x */
548     return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
549 }
550
551
552 /* Returns the weight in a single slab, also calculates the Gaussian- and mass-
553  * weighted sum of positions for that slab */
554 static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
555 {
556     rvec curr_x;              /* The position of an atom                      */
557     rvec curr_x_weighted;     /* The gaussian-weighted position               */
558     real gaussian;            /* A single gaussian weight                     */
559     real wgauss;              /* gaussian times current mass                  */
560     real slabweight = 0.0;    /* The sum of weights in the slab               */
561     int i,islab;
562     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data      */
563
564     
565     erg=rotg->enfrotgrp;
566     clear_rvec(*x_weighted_sum);
567     
568     /* Slab index */
569     islab = j - erg->slab_first;
570     
571     /* Loop over all atoms in the rotation group */
572      for (i=0; i<rotg->nat; i++)
573      {
574          copy_rvec(xc[i], curr_x);
575          gaussian = gaussian_weight(curr_x, rotg, j);
576          wgauss = gaussian * mc[i];
577          svmul(wgauss, curr_x, curr_x_weighted);
578          rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
579          slabweight += wgauss;
580      } /* END of loop over rotation group atoms */
581
582      return slabweight;
583 }
584
585
586 static void get_slab_centers(
587         t_rotgrp *rotg,       /* The rotation group information               */
588         rvec      *xc,        /* The rotation group positions; will 
589                                  typically be enfrotgrp->xc, but at first call 
590                                  it is enfrotgrp->xc_ref                      */
591         real      *mc,        /* The masses of the rotation group atoms       */
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 ( (NULL != out_slabs) && 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_rotgrp  *rotg)
1580 {
1581    int slab, homeslab;
1582    real g;
1583    int count = 0;
1584    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
1585
1586    
1587    erg=rotg->enfrotgrp;
1588    
1589    /* Determine the 'home' slab of this atom: */
1590    homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
1591
1592    /* First determine the weight in the atoms home slab: */
1593    g = gaussian_weight(curr_x, rotg, homeslab);
1594    
1595    erg->gn_atom[count] = g;
1596    erg->gn_slabind[count] = homeslab;
1597    count++;
1598    
1599    
1600    /* Determine the max slab */
1601    slab = homeslab;
1602    while (g > rotg->min_gaussian)
1603    {
1604        slab++;
1605        g = gaussian_weight(curr_x, rotg, slab);
1606        erg->gn_slabind[count]=slab;
1607        erg->gn_atom[count]=g;
1608        count++;
1609    }
1610    count--;
1611    
1612    /* Determine the max slab */
1613    slab = homeslab;
1614    do
1615    {
1616        slab--;
1617        g = gaussian_weight(curr_x, rotg, slab);       
1618        erg->gn_slabind[count]=slab;
1619        erg->gn_atom[count]=g;
1620        count++;
1621    }
1622    while (g > rotg->min_gaussian);
1623    count--;
1624    
1625    return count;
1626 }
1627
1628
1629 static void flex2_precalc_inner_sum(t_rotgrp *rotg)
1630 {
1631     int  i,n,islab;
1632     rvec  xi;                /* positions in the i-sum                        */
1633     rvec  xcn, ycn;          /* the current and the reference slab centers    */
1634     real gaussian_xi;
1635     rvec yi0;
1636     rvec  rin;               /* Helper variables                              */
1637     real  fac,fac2;
1638     rvec innersumvec;
1639     real OOpsii,OOpsiistar;
1640     real sin_rin;          /* s_ii.r_ii */
1641     rvec s_in,tmpvec,tmpvec2;
1642     real mi,wi;            /* Mass-weighting of the positions                 */
1643     real N_M;              /* N/M                                             */
1644     gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
1645
1646
1647     erg=rotg->enfrotgrp;
1648     N_M = rotg->nat * erg->invmass;
1649
1650     /* Loop over all slabs that contain something */
1651     for (n=erg->slab_first; n <= erg->slab_last; n++)
1652     {
1653         islab = n - erg->slab_first; /* slab index */
1654
1655         /* The current center of this slab is saved in xcn: */
1656         copy_rvec(erg->slab_center[islab], xcn);
1657         /* ... and the reference center in ycn: */
1658         copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1659
1660         /*** D. Calculate the whole inner sum used for second and third sum */
1661         /* For slab n, we need to loop over all atoms i again. Since we sorted
1662          * the atoms with respect to the rotation vector, we know that it is sufficient
1663          * to calculate from firstatom to lastatom only. All other contributions will
1664          * be very small. */
1665         clear_rvec(innersumvec);
1666         for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
1667         {
1668             /* Coordinate xi of this atom */
1669             copy_rvec(erg->xc[i],xi);
1670
1671             /* The i-weights */
1672             gaussian_xi = gaussian_weight(xi,rotg,n);
1673             mi = erg->mc_sorted[i];  /* need the sorted mass here */
1674             wi = N_M*mi;
1675
1676             /* Calculate rin */
1677             copy_rvec(erg->xc_ref_sorted[i],yi0); /* Reference position yi0   */
1678             rvec_sub(yi0, ycn, tmpvec2);          /* tmpvec2 = yi0 - ycn      */
1679             mvmul(erg->rotmat, tmpvec2, rin);     /* rin = Omega.(yi0 - ycn)  */
1680
1681             /* Calculate psi_i* and sin */
1682             rvec_sub(xi, xcn, tmpvec2);           /* tmpvec2 = xi - xcn       */
1683             cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xi - xcn)  */
1684             OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
1685             OOpsii = norm(tmpvec);                /* OOpsii = 1 / psii = |v x (xi - xcn)| */
1686
1687                                        /*         v x (xi - xcn)          */
1688             unitv(tmpvec, s_in);       /*  sin = ----------------         */
1689                                        /*        |v x (xi - xcn)|         */
1690
1691             sin_rin=iprod(s_in,rin);   /* sin_rin = sin . rin             */
1692
1693             /* Now the whole sum */
1694             fac = OOpsii/OOpsiistar;
1695             svmul(fac, rin, tmpvec);
1696             fac2 = fac*fac*OOpsii;
1697             svmul(fac2*sin_rin, s_in, tmpvec2);
1698             rvec_dec(tmpvec, tmpvec2);
1699
1700             svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
1701
1702             rvec_inc(innersumvec,tmpvec2);
1703         } /* now we have the inner sum, used both for sum2 and sum3 */
1704
1705         /* Save it to be used in do_flex2_lowlevel */
1706         copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
1707     } /* END of loop over slabs */
1708 }
1709
1710
1711 static void flex_precalc_inner_sum(t_rotgrp *rotg)
1712 {
1713     int   i,n,islab;
1714     rvec  xi;                /* position                                      */
1715     rvec  xcn, ycn;          /* the current and the reference slab centers    */
1716     rvec  qin,rin;           /* q_i^n and r_i^n                               */
1717     real  bin;
1718     rvec  tmpvec;
1719     rvec  innersumvec;       /* Inner part of sum_n2                          */
1720     real  gaussian_xi;       /* Gaussian weight gn(xi)                        */
1721     real  mi,wi;             /* Mass-weighting of the positions               */
1722     real  N_M;               /* N/M                                           */
1723
1724     gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
1725
1726
1727     erg=rotg->enfrotgrp;
1728     N_M = rotg->nat * erg->invmass;
1729
1730     /* Loop over all slabs that contain something */
1731     for (n=erg->slab_first; n <= erg->slab_last; n++)
1732     {
1733         islab = n - erg->slab_first; /* slab index */
1734
1735         /* The current center of this slab is saved in xcn: */
1736         copy_rvec(erg->slab_center[islab], xcn);
1737         /* ... and the reference center in ycn: */
1738         copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1739
1740         /* For slab n, we need to loop over all atoms i again. Since we sorted
1741          * the atoms with respect to the rotation vector, we know that it is sufficient
1742          * to calculate from firstatom to lastatom only. All other contributions will
1743          * be very small. */
1744         clear_rvec(innersumvec);
1745         for (i=erg->firstatom[islab]; i<=erg->lastatom[islab]; i++)
1746         {
1747             /* Coordinate xi of this atom */
1748             copy_rvec(erg->xc[i],xi);
1749
1750             /* The i-weights */
1751             gaussian_xi = gaussian_weight(xi,rotg,n);
1752             mi = erg->mc_sorted[i];  /* need the sorted mass here */
1753             wi = N_M*mi;
1754
1755             /* Calculate rin and qin */
1756             rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
1757             mvmul(erg->rotmat, tmpvec, rin);      /* rin = Omega.(yi0 - ycn)  */
1758             cprod(rotg->vec, rin, tmpvec);    /* tmpvec = v x Omega*(yi0-ycn) */
1759
1760                                              /*        v x Omega*(yi0-ycn)    */
1761             unitv(tmpvec, qin);              /* qin = ---------------------   */
1762                                              /*       |v x Omega*(yi0-ycn)|   */
1763
1764             /* Calculate bin */
1765             rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
1766             bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
1767
1768             svmul(wi*gaussian_xi*bin, qin, tmpvec);
1769
1770             /* Add this contribution to the inner sum: */
1771             rvec_add(innersumvec, tmpvec, innersumvec);
1772         } /* now we have the inner sum vector S^n for this slab */
1773         /* Save it to be used in do_flex_lowlevel */
1774         copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
1775     }
1776 }
1777
1778
1779 static real do_flex2_lowlevel(
1780         t_rotgrp  *rotg,
1781         real      sigma,    /* The Gaussian width sigma */
1782         rvec      x[],
1783         gmx_bool  bOutstepRot,
1784         gmx_bool  bOutstepSlab,
1785         matrix    box)
1786 {
1787     int  count,ic,ii,j,m,n,islab,iigrp,ifit;
1788     rvec xj;                 /* position in the i-sum                         */
1789     rvec yj0;                /* the reference position in the j-sum           */
1790     rvec xcn, ycn;           /* the current and the reference slab centers    */
1791     real V;                  /* This node's part of the rotation pot. energy  */
1792     real gaussian_xj;        /* Gaussian weight                               */
1793     real beta;
1794
1795     real  numerator,fit_numerator;
1796     rvec  rjn,fit_rjn;       /* Helper variables                              */
1797     real  fac,fac2;
1798
1799     real OOpsij,OOpsijstar;
1800     real OOsigma2;           /* 1/(sigma^2)                                   */
1801     real sjn_rjn;
1802     real betasigpsi;
1803     rvec sjn,tmpvec,tmpvec2,yj0_ycn;
1804     rvec sum1vec_part,sum1vec,sum2vec_part,sum2vec,sum3vec,sum4vec,innersumvec;
1805     real sum3,sum4;
1806     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
1807     real mj,wj;              /* Mass-weighting of the positions               */
1808     real N_M;                /* N/M                                           */
1809     real Wjn;                /* g_n(x_j) m_j / Mjn                            */
1810     gmx_bool bCalcPotFit;
1811
1812     /* To calculate the torque per slab */
1813     rvec slab_force;         /* Single force from slab n on one atom          */
1814     rvec slab_sum1vec_part;
1815     real slab_sum3part,slab_sum4part;
1816     rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
1817
1818
1819     erg=rotg->enfrotgrp;
1820
1821     /* Pre-calculate the inner sums, so that we do not have to calculate
1822      * them again for every atom */
1823     flex2_precalc_inner_sum(rotg);
1824
1825     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
1826
1827     /********************************************************/
1828     /* Main loop over all local atoms of the rotation group */
1829     /********************************************************/
1830     N_M = rotg->nat * erg->invmass;
1831     V = 0.0;
1832     OOsigma2 = 1.0 / (sigma*sigma);
1833     for (j=0; j<erg->nat_loc; j++)
1834     {
1835         /* Local index of a rotation group atom  */
1836         ii = erg->ind_loc[j];
1837         /* Position of this atom in the collective array */
1838         iigrp = erg->xc_ref_ind[j];
1839         /* Mass-weighting */
1840         mj = erg->mc[iigrp];  /* need the unsorted mass here */
1841         wj = N_M*mj;
1842         
1843         /* Current position of this atom: x[ii][XX/YY/ZZ]
1844          * Note that erg->xc_center contains the center of mass in case the flex2-t
1845          * potential was chosen. For the flex2 potential erg->xc_center must be
1846          * zero. */
1847         rvec_sub(x[ii], erg->xc_center, xj);
1848
1849         /* Shift this atom such that it is near its reference */
1850         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
1851
1852         /* Determine the slabs to loop over, i.e. the ones with contributions
1853          * larger than min_gaussian */
1854         count = get_single_atom_gaussians(xj, rotg);
1855         
1856         clear_rvec(sum1vec_part);
1857         clear_rvec(sum2vec_part);
1858         sum3 = 0.0;
1859         sum4 = 0.0;
1860         /* Loop over the relevant slabs for this atom */
1861         for (ic=0; ic < count; ic++)  
1862         {
1863             n = erg->gn_slabind[ic];
1864             
1865             /* Get the precomputed Gaussian value of curr_slab for curr_x */
1866             gaussian_xj = erg->gn_atom[ic];
1867
1868             islab = n - erg->slab_first; /* slab index */
1869             
1870             /* The (unrotated) reference position of this atom is copied to yj0: */
1871             copy_rvec(rotg->x_ref[iigrp], yj0);
1872
1873             beta = calc_beta(xj, rotg,n);
1874
1875             /* The current center of this slab is saved in xcn: */
1876             copy_rvec(erg->slab_center[islab], xcn);
1877             /* ... and the reference center in ycn: */
1878             copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
1879             
1880             rvec_sub(yj0, ycn, yj0_ycn);          /* yj0_ycn = yj0 - ycn      */
1881
1882             /* Rotate: */
1883             mvmul(erg->rotmat, yj0_ycn, rjn);     /* rjn = Omega.(yj0 - ycn)  */
1884             
1885             /* Subtract the slab center from xj */
1886             rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
1887
1888             /* Calculate sjn */
1889             cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xj - xcn)  */
1890
1891             OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
1892
1893             numerator = sqr(iprod(tmpvec, rjn));
1894             
1895             /*********************************/
1896             /* Add to the rotation potential */
1897             /*********************************/
1898             V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
1899
1900             /* If requested, also calculate the potential for a set of angles
1901              * near the current reference angle */
1902             if (bCalcPotFit)
1903             {
1904                 for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
1905                 {
1906                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
1907                     fit_numerator = sqr(iprod(tmpvec, fit_rjn));
1908                     erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
1909                 }
1910             }
1911
1912             /*************************************/
1913             /* Now calculate the force on atom j */
1914             /*************************************/
1915
1916             OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
1917
1918                                            /*         v x (xj - xcn)          */
1919             unitv(tmpvec, sjn);            /*  sjn = ----------------         */
1920                                            /*        |v x (xj - xcn)|         */
1921
1922             sjn_rjn=iprod(sjn,rjn);        /* sjn_rjn = sjn . rjn             */
1923
1924
1925             /*** A. Calculate the first of the four sum terms: ****************/
1926             fac = OOpsij/OOpsijstar;
1927             svmul(fac, rjn, tmpvec);
1928             fac2 = fac*fac*OOpsij;
1929             svmul(fac2*sjn_rjn, sjn, tmpvec2);
1930             rvec_dec(tmpvec, tmpvec2);
1931             fac2 = wj*gaussian_xj; /* also needed for sum4 */
1932             svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
1933             /********************/
1934             /*** Add to sum1: ***/
1935             /********************/
1936             rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
1937
1938             /*** B. Calculate the forth of the four sum terms: ****************/
1939             betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
1940             /********************/
1941             /*** Add to sum4: ***/
1942             /********************/
1943             slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
1944             sum4 += slab_sum4part;
1945
1946             /*** C. Calculate Wjn for second and third sum */
1947             /* Note that we can safely divide by slab_weights since we check in
1948              * get_slab_centers that it is non-zero. */
1949             Wjn = gaussian_xj*mj/erg->slab_weights[islab];
1950
1951             /* We already have precalculated the inner sum for slab n */
1952             copy_rvec(erg->slab_innersumvec[islab], innersumvec);
1953
1954             /* Weigh the inner sum vector with Wjn */
1955             svmul(Wjn, innersumvec, innersumvec);
1956
1957             /*** E. Calculate the second of the four sum terms: */
1958             /********************/
1959             /*** Add to sum2: ***/
1960             /********************/
1961             rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
1962             
1963             /*** F. Calculate the third of the four sum terms: */
1964             slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
1965             sum3 += slab_sum3part; /* still needs to be multiplied with v */
1966
1967             /*** G. Calculate the torque on the local slab's axis: */
1968             if (bOutstepRot)
1969             {
1970                 /* Sum1 */
1971                 cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
1972                 /* Sum2 */
1973                 cprod(innersumvec, rotg->vec, slab_sum2vec);
1974                 /* Sum3 */
1975                 svmul(slab_sum3part, rotg->vec, slab_sum3vec);
1976                 /* Sum4 */
1977                 svmul(slab_sum4part, rotg->vec, slab_sum4vec);
1978
1979                 /* The force on atom ii from slab n only: */
1980                 for (m=0; m<DIM; m++)
1981                     slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
1982
1983                 erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
1984             }
1985         } /* END of loop over slabs */
1986
1987         /* Construct the four individual parts of the vector sum: */
1988         cprod(sum1vec_part, rotg->vec, sum1vec);      /* sum1vec =   { } x v  */
1989         cprod(sum2vec_part, rotg->vec, sum2vec);      /* sum2vec =   { } x v  */
1990         svmul(sum3, rotg->vec, sum3vec);              /* sum3vec =   { } . v  */
1991         svmul(sum4, rotg->vec, sum4vec);              /* sum4vec =   { } . v  */
1992
1993         /* Store the additional force so that it can be added to the force
1994          * array after the normal forces have been evaluated */
1995         for (m=0; m<DIM; m++)
1996             erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
1997
1998 #ifdef SUM_PARTS
1999         fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n",    -rotg->k*sum1vec[XX],    -rotg->k*sum1vec[YY],    -rotg->k*sum1vec[ZZ]);
2000         fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n",     rotg->k*sum2vec[XX],     rotg->k*sum2vec[YY],     rotg->k*sum2vec[ZZ]);
2001         fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n",    -rotg->k*sum3vec[XX],    -rotg->k*sum3vec[YY],    -rotg->k*sum3vec[ZZ]);
2002         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]);
2003 #endif
2004
2005         PRINT_FORCE_J
2006
2007     } /* END of loop over local atoms */
2008
2009     return V;
2010 }
2011
2012
2013 static real do_flex_lowlevel(
2014         t_rotgrp *rotg,
2015         real      sigma,     /* The Gaussian width sigma                      */
2016         rvec      x[],
2017         gmx_bool  bOutstepRot,
2018         gmx_bool  bOutstepSlab,
2019         matrix    box)
2020 {
2021     int   count,ic,ifit,ii,j,m,n,islab,iigrp;
2022     rvec  xj,yj0;            /* current and reference position                */
2023     rvec  xcn, ycn;          /* the current and the reference slab centers    */
2024     rvec  yj0_ycn;           /* yj0 - ycn                                     */
2025     rvec  xj_xcn;            /* xj - xcn                                      */
2026     rvec  qjn,fit_qjn;       /* q_i^n                                         */
2027     rvec  sum_n1,sum_n2;     /* Two contributions to the rotation force       */
2028     rvec  innersumvec;       /* Inner part of sum_n2                          */
2029     rvec  s_n;
2030     rvec  force_n;           /* Single force from slab n on one atom          */
2031     rvec  force_n1,force_n2; /* First and second part of force_n              */
2032     rvec  tmpvec,tmpvec2,tmp_f;   /* Helper variables                         */
2033     real  V;                 /* The rotation potential energy                 */
2034     real  OOsigma2;          /* 1/(sigma^2)                                   */
2035     real  beta;              /* beta_n(xj)                                    */
2036     real  bjn, fit_bjn;      /* b_j^n                                         */
2037     real  gaussian_xj;       /* Gaussian weight gn(xj)                        */
2038     real  betan_xj_sigma2;
2039     real  mj,wj;             /* Mass-weighting of the positions               */
2040     real  N_M;               /* N/M                                           */
2041     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
2042     gmx_bool bCalcPotFit;
2043
2044     
2045     erg=rotg->enfrotgrp;
2046
2047     /* Pre-calculate the inner sums, so that we do not have to calculate
2048      * them again for every atom */
2049     flex_precalc_inner_sum(rotg);
2050
2051     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
2052
2053     /********************************************************/
2054     /* Main loop over all local atoms of the rotation group */
2055     /********************************************************/
2056     OOsigma2 = 1.0/(sigma*sigma);
2057     N_M = rotg->nat * erg->invmass;
2058     V = 0.0;
2059     for (j=0; j<erg->nat_loc; j++)
2060     {
2061         /* Local index of a rotation group atom  */
2062         ii = erg->ind_loc[j];
2063         /* Position of this atom in the collective array */
2064         iigrp = erg->xc_ref_ind[j];
2065         /* Mass-weighting */
2066         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2067         wj = N_M*mj;
2068         
2069         /* Current position of this atom: x[ii][XX/YY/ZZ]
2070          * Note that erg->xc_center contains the center of mass in case the flex-t
2071          * potential was chosen. For the flex potential erg->xc_center must be
2072          * zero. */
2073         rvec_sub(x[ii], erg->xc_center, xj);
2074         
2075         /* Shift this atom such that it is near its reference */
2076         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2077
2078         /* Determine the slabs to loop over, i.e. the ones with contributions
2079          * larger than min_gaussian */
2080         count = get_single_atom_gaussians(xj, rotg);
2081
2082         clear_rvec(sum_n1);
2083         clear_rvec(sum_n2);
2084
2085         /* Loop over the relevant slabs for this atom */
2086         for (ic=0; ic < count; ic++)  
2087         {
2088             n = erg->gn_slabind[ic];
2089                 
2090             /* Get the precomputed Gaussian for xj in slab n */
2091             gaussian_xj = erg->gn_atom[ic];
2092
2093             islab = n - erg->slab_first; /* slab index */
2094             
2095             /* The (unrotated) reference position of this atom is saved in yj0: */
2096             copy_rvec(rotg->x_ref[iigrp], yj0);
2097
2098             beta = calc_beta(xj, rotg, n);
2099
2100             /* The current center of this slab is saved in xcn: */
2101             copy_rvec(erg->slab_center[islab], xcn);
2102             /* ... and the reference center in ycn: */
2103             copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
2104             
2105             rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
2106
2107             /* Rotate: */
2108             mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
2109             
2110             /* Subtract the slab center from xj */
2111             rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
2112             
2113             /* Calculate qjn */
2114             cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
2115
2116                                  /*         v x Omega.(yj0-ycn)    */
2117             unitv(tmpvec,qjn);   /*  qjn = ---------------------   */
2118                                  /*        |v x Omega.(yj0-ycn)|   */
2119
2120             bjn = iprod(qjn, xj_xcn);   /* bjn = qjn * (xj - xcn) */
2121             
2122             /*********************************/
2123             /* Add to the rotation potential */
2124             /*********************************/
2125             V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
2126             
2127             /* If requested, also calculate the potential for a set of angles
2128              * near the current reference angle */
2129             if (bCalcPotFit)
2130             {
2131                 for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
2132                 {
2133                     /* As above calculate Omega.(yj0-ycn), now for the other angles */
2134                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
2135                     /* As above calculate qjn */
2136                     cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
2137                                              /*             v x Omega.(yj0-ycn)    */
2138                     unitv(tmpvec,fit_qjn);   /*  fit_qjn = ---------------------   */
2139                                              /*            |v x Omega.(yj0-ycn)|   */
2140                     fit_bjn = iprod(fit_qjn, xj_xcn);   /* fit_bjn = fit_qjn * (xj - xcn) */
2141                     /* Add to the rotation potential for this angle */
2142                     erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*sqr(fit_bjn);
2143                 }
2144             }
2145
2146             /****************************************************************/
2147             /* sum_n1 will typically be the main contribution to the force: */
2148             /****************************************************************/
2149             betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
2150
2151             /* The next lines calculate
2152              *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
2153             svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
2154             rvec_sub(qjn,tmpvec2,tmpvec);
2155
2156             /* Multiply with gn(xj)*bjn: */
2157             svmul(gaussian_xj*bjn,tmpvec,tmpvec2);
2158
2159             /* Sum over n: */
2160             rvec_inc(sum_n1,tmpvec2);
2161             
2162             /* We already have precalculated the Sn term for slab n */
2163             copy_rvec(erg->slab_innersumvec[islab], s_n);
2164                                                                           /*          beta_n(xj)              */
2165             svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
2166                                                                           /*            sigma^2               */
2167
2168             rvec_sub(s_n, tmpvec, innersumvec);
2169             
2170             /* We can safely divide by slab_weights since we check in get_slab_centers
2171              * that it is non-zero. */
2172             svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
2173
2174             rvec_add(sum_n2, innersumvec, sum_n2);
2175             
2176             /* Calculate the torque: */
2177             if (bOutstepRot)
2178             {
2179                 /* The force on atom ii from slab n only: */
2180                 svmul(-rotg->k*wj, tmpvec2    , force_n1); /* part 1 */
2181                 svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
2182                 rvec_add(force_n1, force_n2, force_n);
2183                 erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
2184             }
2185         } /* END of loop over slabs */
2186
2187         /* Put both contributions together: */
2188         svmul(wj, sum_n1, sum_n1);
2189         svmul(mj, sum_n2, sum_n2);
2190         rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
2191
2192         /* Store the additional force so that it can be added to the force
2193          * array after the normal forces have been evaluated */
2194         for(m=0; m<DIM; m++)
2195             erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
2196
2197         PRINT_FORCE_J
2198
2199     } /* END of loop over local atoms */
2200
2201     return V;
2202 }
2203
2204 #ifdef PRINT_COORDS
2205 static void print_coordinates(t_rotgrp *rotg, rvec x[], matrix box, int step)
2206 {
2207     int i;
2208     static FILE *fp;
2209     static char buf[STRLEN];
2210     static gmx_bool bFirst=1;
2211
2212
2213     if (bFirst)
2214     {
2215         sprintf(buf, "coords%d.txt", cr->nodeid);
2216         fp = fopen(buf, "w");
2217         bFirst = 0;
2218     }
2219
2220     fprintf(fp, "\nStep %d\n", step);
2221     fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
2222             box[XX][XX], box[XX][YY], box[XX][ZZ],
2223             box[YY][XX], box[YY][YY], box[YY][ZZ],
2224             box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
2225     for (i=0; i<rotg->nat; i++)
2226     {
2227         fprintf(fp, "%4d  %f %f %f\n", i,
2228                 erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
2229     }
2230     fflush(fp);
2231
2232 }
2233 #endif
2234
2235
2236 static int projection_compare(const void *a, const void *b)
2237 {
2238     sort_along_vec_t *xca, *xcb;
2239     
2240     
2241     xca = (sort_along_vec_t *)a;
2242     xcb = (sort_along_vec_t *)b;
2243     
2244     if (xca->xcproj < xcb->xcproj)
2245         return -1;
2246     else if (xca->xcproj > xcb->xcproj)
2247         return 1;
2248     else
2249         return 0;
2250 }
2251
2252
2253 static void sort_collective_coordinates(
2254         t_rotgrp *rotg,         /* Rotation group */
2255         sort_along_vec_t *data) /* Buffer for sorting the positions */
2256 {
2257     int i;
2258     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2259
2260     
2261     erg=rotg->enfrotgrp;
2262     
2263     /* The projection of the position vector on the rotation vector is
2264      * the relevant value for sorting. Fill the 'data' structure */
2265     for (i=0; i<rotg->nat; i++)
2266     {
2267         data[i].xcproj = iprod(erg->xc[i], rotg->vec);  /* sort criterium */
2268         data[i].m      = erg->mc[i];
2269         data[i].ind    = i;
2270         copy_rvec(erg->xc[i]    , data[i].x    );
2271         copy_rvec(rotg->x_ref[i], data[i].x_ref);
2272     }
2273     /* Sort the 'data' structure */
2274     gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
2275     
2276     /* Copy back the sorted values */
2277     for (i=0; i<rotg->nat; i++)
2278     {
2279         copy_rvec(data[i].x    , erg->xc[i]           );
2280         copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
2281         erg->mc_sorted[i]  = data[i].m;
2282         erg->xc_sortind[i] = data[i].ind;
2283     }
2284 }
2285
2286
2287 /* For each slab, get the first and the last index of the sorted atom
2288  * indices */
2289 static void get_firstlast_atom_per_slab(t_rotgrp *rotg)
2290 {
2291     int i,islab,n;
2292     real beta;
2293     gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
2294
2295     
2296     erg=rotg->enfrotgrp;
2297
2298     /* Find the first atom that needs to enter the calculation for each slab */
2299     n = erg->slab_first;  /* slab */
2300     i = 0; /* start with the first atom */
2301     do
2302     {
2303         /* Find the first atom that significantly contributes to this slab */
2304         do /* move forward in position until a large enough beta is found */
2305         {
2306             beta = calc_beta(erg->xc[i], rotg, n);
2307             i++;
2308         } while ((beta < -erg->max_beta) && (i < rotg->nat));
2309         i--;
2310         islab = n - erg->slab_first;  /* slab index */
2311         erg->firstatom[islab] = i;
2312         /* Proceed to the next slab */
2313         n++;
2314     } while (n <= erg->slab_last);
2315     
2316     /* Find the last atom for each slab */
2317      n = erg->slab_last; /* start with last slab */
2318      i = rotg->nat-1;  /* start with the last atom */
2319      do
2320      {
2321          do /* move backward in position until a large enough beta is found */
2322          {
2323              beta = calc_beta(erg->xc[i], rotg, n);
2324              i--;
2325          } while ((beta > erg->max_beta) && (i > -1));
2326          i++;
2327          islab = n - erg->slab_first;  /* slab index */
2328          erg->lastatom[islab] = i;
2329          /* Proceed to the next slab */
2330          n--;
2331      } while (n >= erg->slab_first);
2332 }
2333
2334
2335 /* Determine the very first and very last slab that needs to be considered 
2336  * For the first slab that needs to be considered, we have to find the smallest
2337  * n that obeys:
2338  * 
2339  * x_first * v - n*Delta_x <= beta_max
2340  * 
2341  * slab index n, slab distance Delta_x, rotation vector v. For the last slab we 
2342  * have to find the largest n that obeys
2343  * 
2344  * x_last * v - n*Delta_x >= -beta_max
2345  *  
2346  */
2347 static gmx_inline int get_first_slab(
2348         t_rotgrp *rotg,     /* The rotation group (inputrec data) */
2349         real     max_beta,  /* The max_beta value, instead of min_gaussian */
2350         rvec     firstatom) /* First atom after sorting along the rotation vector v */
2351 {
2352     /* Find the first slab for the first atom */   
2353     return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
2354 }
2355
2356
2357 static gmx_inline int get_last_slab(
2358         t_rotgrp *rotg,     /* The rotation group (inputrec data) */
2359         real     max_beta,  /* The max_beta value, instead of min_gaussian */
2360         rvec     lastatom)  /* Last atom along v */
2361 {
2362     /* Find the last slab for the last atom */
2363     return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);    
2364 }
2365
2366
2367 static void get_firstlast_slab_check(
2368         t_rotgrp        *rotg,     /* The rotation group (inputrec data) */
2369         t_gmx_enfrotgrp *erg,      /* The rotation group (data only accessible in this file) */
2370         rvec            firstatom, /* First atom after sorting along the rotation vector v */
2371         rvec            lastatom,  /* Last atom along v */
2372         int             g)         /* The rotation group number */
2373 {
2374     erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
2375     erg->slab_last  = get_last_slab(rotg, erg->max_beta, lastatom);
2376
2377     /* Check whether we have reference data to compare against */
2378     if (erg->slab_first < erg->slab_first_ref)
2379         gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
2380                   RotStr, erg->slab_first);
2381     
2382     /* Check whether we have reference data to compare against */
2383     if (erg->slab_last > erg->slab_last_ref)
2384         gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
2385                   RotStr, erg->slab_last);
2386 }
2387
2388
2389 /* Enforced rotation with a flexible axis */
2390 static void do_flexible(
2391         gmx_bool  bMaster,
2392         gmx_enfrot_t enfrot,    /* Other rotation data                        */
2393         t_rotgrp  *rotg,        /* The rotation group                         */
2394         int       g,            /* Group number                               */
2395         rvec      x[],          /* The local positions                        */
2396         matrix    box,
2397         double    t,            /* Time in picoseconds                        */
2398         gmx_large_int_t step,   /* The time step                              */
2399         gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
2400         gmx_bool  bOutstepSlab) /* Output per-slab data                       */
2401 {
2402     int          l,nslabs;
2403     real         sigma;       /* The Gaussian width sigma */
2404     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
2405
2406     
2407     erg=rotg->enfrotgrp;
2408
2409     /* Define the sigma value */
2410     sigma = 0.7*rotg->slab_dist;
2411     
2412     /* Sort the collective coordinates erg->xc along the rotation vector. This is
2413      * an optimization for the inner loop. */
2414     sort_collective_coordinates(rotg, enfrot->data);
2415     
2416     /* Determine the first relevant slab for the first atom and the last
2417      * relevant slab for the last atom */
2418     get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g);
2419     
2420     /* Determine for each slab depending on the min_gaussian cutoff criterium,
2421      * a first and a last atom index inbetween stuff needs to be calculated */
2422     get_firstlast_atom_per_slab(rotg);
2423
2424     /* Determine the gaussian-weighted center of positions for all slabs */
2425     get_slab_centers(rotg,erg->xc,erg->mc_sorted,g,t,enfrot->out_slabs,bOutstepSlab,FALSE);
2426         
2427     /* Clear the torque per slab from last time step: */
2428     nslabs = erg->slab_last - erg->slab_first + 1;
2429     for (l=0; l<nslabs; l++)
2430         erg->slab_torque_v[l] = 0.0;
2431     
2432     /* Call the rotational forces kernel */
2433     if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
2434         erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
2435     else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
2436         erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
2437     else
2438         gmx_fatal(FARGS, "Unknown flexible rotation type");
2439     
2440     /* Determine angle by RMSD fit to the reference - Let's hope this */
2441     /* only happens once in a while, since this is not parallelized! */
2442     if ( bMaster && (erotgFitPOT != rotg->eFittype) )
2443     {
2444         if (bOutstepRot)
2445         {
2446             /* Fit angle of the whole rotation group */
2447             erg->angle_v = flex_fit_angle(rotg);
2448         }
2449         if (bOutstepSlab)
2450         {
2451             /* Fit angle of each slab */
2452             flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
2453         }
2454     }
2455
2456     /* Lump together the torques from all slabs: */
2457     erg->torque_v = 0.0;
2458     for (l=0; l<nslabs; l++)
2459          erg->torque_v += erg->slab_torque_v[l];
2460 }
2461
2462
2463 /* Calculate the angle between reference and actual rotation group atom,
2464  * both projected into a plane perpendicular to the rotation vector: */
2465 static void angle(t_rotgrp *rotg,
2466         rvec x_act,
2467         rvec x_ref,
2468         real *alpha,
2469         real *weight)  /* atoms near the rotation axis should count less than atoms far away */
2470 {
2471     rvec xp, xrp;  /* current and reference positions projected on a plane perpendicular to pg->vec */
2472     rvec dum;
2473
2474
2475     /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
2476     /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
2477     svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
2478     rvec_sub(x_ref, dum, xrp);
2479     /* Project x_act: */
2480     svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
2481     rvec_sub(x_act, dum, xp);
2482
2483     /* Retrieve information about which vector precedes. gmx_angle always
2484      * returns a positive angle. */
2485     cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
2486
2487     if (iprod(rotg->vec, dum) >= 0)
2488         *alpha = -gmx_angle(xrp, xp);
2489     else
2490         *alpha = +gmx_angle(xrp, xp);
2491     
2492     /* Also return the weight */
2493     *weight = norm(xp);
2494 }
2495
2496
2497 /* Project first vector onto a plane perpendicular to the second vector 
2498  * dr = dr - (dr.v)v
2499  * Note that v must be of unit length.
2500  */
2501 static gmx_inline void project_onto_plane(rvec dr, const rvec v)
2502 {
2503     rvec tmp;
2504     
2505     
2506     svmul(iprod(dr,v),v,tmp);  /* tmp = (dr.v)v */
2507     rvec_dec(dr, tmp);         /* dr = dr - (dr.v)v */
2508 }
2509
2510
2511 /* Fixed rotation: The rotation reference group rotates around the v axis. */
2512 /* The atoms of the actual rotation group are attached with imaginary  */
2513 /* springs to the reference atoms.                                     */
2514 static void do_fixed(
2515         t_rotgrp  *rotg,        /* The rotation group                         */
2516         rvec      x[],          /* The positions                              */
2517         matrix    box,          /* The simulation box                         */
2518         double    t,            /* Time in picoseconds                        */
2519         gmx_large_int_t step,   /* The time step                              */
2520         gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
2521         gmx_bool  bOutstepSlab) /* Output per-slab data                       */
2522 {
2523     int       ifit,j,jj,m;
2524     rvec      dr;
2525     rvec      tmp_f;           /* Force */
2526     real      alpha;           /* a single angle between an actual and a reference position */
2527     real      weight;          /* single weight for a single angle */
2528     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2529     rvec      xi_xc;           /* xi - xc */
2530     gmx_bool  bCalcPotFit;
2531     rvec      fit_xr_loc;
2532
2533     /* for mass weighting: */
2534     real      wi;              /* Mass-weighting of the positions */
2535     real      N_M;             /* N/M */
2536     real      k_wi;            /* k times wi */
2537
2538     gmx_bool  bProject;
2539
2540     
2541     erg=rotg->enfrotgrp;
2542     bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
2543     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
2544
2545     N_M = rotg->nat * erg->invmass;
2546
2547     /* Each process calculates the forces on its local atoms */
2548     for (j=0; j<erg->nat_loc; j++)
2549     {
2550         /* Calculate (x_i-x_c) resp. (x_i-u) */
2551         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
2552
2553         /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2554         rvec_sub(erg->xr_loc[j], xi_xc, dr);
2555         
2556         if (bProject)
2557             project_onto_plane(dr, rotg->vec);
2558             
2559         /* Mass-weighting */
2560         wi = N_M*erg->m_loc[j];
2561
2562         /* Store the additional force so that it can be added to the force
2563          * array after the normal forces have been evaluated */
2564         k_wi = rotg->k*wi;
2565         for (m=0; m<DIM; m++)
2566         {
2567             tmp_f[m]             = k_wi*dr[m];
2568             erg->f_rot_loc[j][m] = tmp_f[m];
2569             erg->V              += 0.5*k_wi*sqr(dr[m]);
2570         }
2571         
2572         /* If requested, also calculate the potential for a set of angles
2573          * near the current reference angle */
2574         if (bCalcPotFit)
2575         {
2576             for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
2577             {
2578                 /* Index of this rotation group atom with respect to the whole rotation group */
2579                 jj = erg->xc_ref_ind[j];
2580
2581                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2582                  * just for a single local atom */
2583                 mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
2584
2585                 /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2586                 rvec_sub(fit_xr_loc, xi_xc, dr);
2587
2588                 if (bProject)
2589                     project_onto_plane(dr, rotg->vec);
2590
2591                 /* Add to the rotation potential for this angle: */
2592                 erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
2593             }
2594         }
2595
2596         if (bOutstepRot)
2597         {
2598             /* Add to the torque of this rotation group */
2599             erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2600             
2601             /* Calculate the angle between reference and actual rotation group atom. */
2602             angle(rotg, xi_xc, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2603             erg->angle_v  += alpha * weight;
2604             erg->weight_v += weight;
2605         }
2606         /* If you want enforced rotation to contribute to the virial,
2607          * activate the following lines:
2608             if (MASTER(cr))
2609             {
2610                Add the rotation contribution to the virial
2611               for(j=0; j<DIM; j++)
2612                 for(m=0;m<DIM;m++)
2613                   vir[j][m] += 0.5*f[ii][j]*dr[m];
2614             }
2615          */
2616
2617         PRINT_FORCE_J
2618
2619     } /* end of loop over local rotation group atoms */
2620 }
2621
2622
2623 /* Calculate the radial motion potential and forces */
2624 static void do_radial_motion(
2625         t_rotgrp  *rotg,        /* The rotation group                         */
2626         rvec      x[],          /* The positions                              */
2627         matrix    box,          /* The simulation box                         */
2628         double    t,            /* Time in picoseconds                        */
2629         gmx_large_int_t step,   /* The time step                              */
2630         gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
2631         gmx_bool  bOutstepSlab) /* Output per-slab data                       */
2632 {
2633     int       j,jj,ifit;
2634     rvec      tmp_f;           /* Force */
2635     real      alpha;           /* a single angle between an actual and a reference position */
2636     real      weight;          /* single weight for a single angle */
2637     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2638     rvec      xj_u;            /* xj - u */
2639     rvec      tmpvec,fit_tmpvec;
2640     real      fac,fac2,sum=0.0;
2641     rvec      pj;
2642     gmx_bool  bCalcPotFit;
2643
2644     /* For mass weighting: */
2645     real      wj;              /* Mass-weighting of the positions */
2646     real      N_M;             /* N/M */
2647
2648
2649     erg=rotg->enfrotgrp;
2650     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
2651
2652     N_M = rotg->nat * erg->invmass;
2653
2654     /* Each process calculates the forces on its local atoms */
2655     for (j=0; j<erg->nat_loc; j++)
2656     {
2657         /* Calculate (xj-u) */
2658         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
2659
2660         /* Calculate Omega.(yj0-u) */
2661         cprod(rotg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
2662
2663                               /*         v x Omega.(yj0-u)     */
2664         unitv(tmpvec, pj);    /*  pj = ---------------------   */
2665                               /*       | v x Omega.(yj0-u) |   */
2666
2667         fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
2668         fac2 = fac*fac;
2669
2670         /* Mass-weighting */
2671         wj = N_M*erg->m_loc[j];
2672
2673         /* Store the additional force so that it can be added to the force
2674          * array after the normal forces have been evaluated */
2675         svmul(-rotg->k*wj*fac, pj, tmp_f);
2676         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2677         sum += wj*fac2;
2678
2679         /* If requested, also calculate the potential for a set of angles
2680          * near the current reference angle */
2681         if (bCalcPotFit)
2682         {
2683             for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
2684             {
2685                 /* Index of this rotation group atom with respect to the whole rotation group */
2686                 jj = erg->xc_ref_ind[j];
2687
2688                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2689                  * just for a single local atom */
2690                 mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
2691
2692                 /* Calculate Omega.(yj0-u) */
2693                 cprod(rotg->vec, fit_tmpvec, tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
2694                                       /*         v x Omega.(yj0-u)     */
2695                 unitv(tmpvec, pj);    /*  pj = ---------------------   */
2696                                       /*       | v x Omega.(yj0-u) |   */
2697
2698                 fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
2699                 fac2 = fac*fac;
2700
2701                 /* Add to the rotation potential for this angle: */
2702                 erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
2703             }
2704         }
2705
2706         if (bOutstepRot)
2707         {
2708             /* Add to the torque of this rotation group */
2709             erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2710
2711             /* Calculate the angle between reference and actual rotation group atom. */
2712             angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2713             erg->angle_v  += alpha * weight;
2714             erg->weight_v += weight;
2715         }
2716
2717         PRINT_FORCE_J
2718
2719     } /* end of loop over local rotation group atoms */
2720     erg->V = 0.5*rotg->k*sum;
2721 }
2722
2723
2724 /* Calculate the radial motion pivot-free potential and forces */
2725 static void do_radial_motion_pf(
2726         t_rotgrp  *rotg,        /* The rotation group                         */
2727         rvec      x[],          /* The positions                              */
2728         matrix    box,          /* The simulation box                         */
2729         double    t,            /* Time in picoseconds                        */
2730         gmx_large_int_t step,   /* The time step                              */
2731         gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
2732         gmx_bool  bOutstepSlab) /* Output per-slab data                       */
2733 {
2734     int       i,ii,iigrp,ifit,j;
2735     rvec      xj;              /* Current position */
2736     rvec      xj_xc;           /* xj  - xc  */
2737     rvec      yj0_yc0;         /* yj0 - yc0 */
2738     rvec      tmp_f;           /* Force */
2739     real      alpha;           /* a single angle between an actual and a reference position */
2740     real      weight;          /* single weight for a single angle */
2741     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2742     rvec      tmpvec, tmpvec2;
2743     rvec      innersumvec;     /* Precalculation of the inner sum */
2744     rvec      innersumveckM;
2745     real      fac,fac2,V=0.0;
2746     rvec      qi,qj;
2747     gmx_bool  bCalcPotFit;
2748
2749     /* For mass weighting: */
2750     real      mj,wi,wj;        /* Mass-weighting of the positions */
2751     real      N_M;             /* N/M */
2752
2753
2754     erg=rotg->enfrotgrp;
2755     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
2756
2757     N_M = rotg->nat * erg->invmass;
2758
2759     /* Get the current center of the rotation group: */
2760     get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
2761
2762     /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
2763     clear_rvec(innersumvec);
2764     for (i=0; i < rotg->nat; i++)
2765     {
2766         /* Mass-weighting */
2767         wi = N_M*erg->mc[i];
2768
2769         /* Calculate qi. Note that xc_ref_center has already been subtracted from
2770          * x_ref in init_rot_group.*/
2771         mvmul(erg->rotmat, rotg->x_ref[i], tmpvec);  /* tmpvec  = Omega.(yi0-yc0) */
2772
2773         cprod(rotg->vec, tmpvec, tmpvec2);          /* tmpvec2 = v x Omega.(yi0-yc0) */
2774
2775                               /*         v x Omega.(yi0-yc0)     */
2776         unitv(tmpvec2, qi);   /*  qi = -----------------------   */
2777                               /*       | v x Omega.(yi0-yc0) |   */
2778
2779         rvec_sub(erg->xc[i], erg->xc_center, tmpvec);  /* tmpvec = xi-xc */
2780
2781         svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
2782
2783         rvec_inc(innersumvec, tmpvec2);
2784     }
2785     svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
2786
2787     /* Each process calculates the forces on its local atoms */
2788     for (j=0; j<erg->nat_loc; j++)
2789     {
2790         /* Local index of a rotation group atom  */
2791         ii = erg->ind_loc[j];
2792         /* Position of this atom in the collective array */
2793         iigrp = erg->xc_ref_ind[j];
2794         /* Mass-weighting */
2795         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2796         wj = N_M*mj;
2797
2798         /* Current position of this atom: x[ii][XX/YY/ZZ] */
2799         copy_rvec(x[ii], xj);
2800
2801         /* Shift this atom such that it is near its reference */
2802         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2803
2804         /* The (unrotated) reference position is yj0. yc0 has already
2805          * been subtracted in init_rot_group */
2806         copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
2807
2808         /* Calculate Omega.(yj0-yc0) */
2809         mvmul(erg->rotmat, yj0_yc0, tmpvec2);     /* tmpvec2 = Omega.(yj0 - yc0)  */
2810
2811         cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
2812
2813                               /*         v x Omega.(yj0-yc0)     */
2814         unitv(tmpvec, qj);    /*  qj = -----------------------   */
2815                               /*       | v x Omega.(yj0-yc0) |   */
2816
2817         /* Calculate (xj-xc) */
2818         rvec_sub(xj, erg->xc_center, xj_xc);  /* xj_xc = xj-xc */
2819
2820         fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
2821         fac2 = fac*fac;
2822
2823         /* Store the additional force so that it can be added to the force
2824          * array after the normal forces have been evaluated */
2825         svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
2826         svmul(mj, innersumveckM, tmpvec);  /* part 2 of force */
2827         rvec_inc(tmp_f, tmpvec);
2828         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2829         V += wj*fac2;
2830
2831         /* If requested, also calculate the potential for a set of angles
2832          * near the current reference angle */
2833         if (bCalcPotFit)
2834         {
2835             for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
2836             {
2837                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2838                  * just for a single local atom */
2839                 mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
2840
2841                 /* Calculate Omega.(yj0-u) */
2842                 cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
2843                                       /*         v x Omega.(yj0-yc0)     */
2844                 unitv(tmpvec, qj);    /*  qj = -----------------------   */
2845                                       /*       | v x Omega.(yj0-yc0) |   */
2846
2847                 fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
2848                 fac2 = fac*fac;
2849
2850                 /* Add to the rotation potential for this angle: */
2851                 erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
2852             }
2853         }
2854
2855         if (bOutstepRot)
2856         {
2857             /* Add to the torque of this rotation group */
2858             erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
2859
2860             /* Calculate the angle between reference and actual rotation group atom. */
2861             angle(rotg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
2862             erg->angle_v  += alpha * weight;
2863             erg->weight_v += weight;
2864         }
2865
2866         PRINT_FORCE_J
2867
2868     } /* end of loop over local rotation group atoms */
2869     erg->V = 0.5*rotg->k*V;
2870 }
2871
2872
2873 /* Precalculate the inner sum for the radial motion 2 forces */
2874 static void radial_motion2_precalc_inner_sum(t_rotgrp  *rotg, rvec innersumvec)
2875 {
2876     int       i;
2877     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2878     rvec      xi_xc;           /* xj - xc */
2879     rvec      tmpvec,tmpvec2;
2880     real      fac,fac2;
2881     rvec      ri,si;
2882     real      siri;
2883     rvec      v_xi_xc;          /* v x (xj - u) */
2884     real      psii,psiistar;
2885     real      wi;              /* Mass-weighting of the positions */
2886     real      N_M;             /* N/M */
2887     rvec      sumvec;
2888
2889     erg=rotg->enfrotgrp;
2890     N_M = rotg->nat * erg->invmass;
2891
2892     /* Loop over the collective set of positions */
2893     clear_rvec(sumvec);
2894     for (i=0; i<rotg->nat; i++)
2895     {
2896         /* Mass-weighting */
2897         wi = N_M*erg->mc[i];
2898
2899         rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
2900
2901         /* Calculate ri. Note that xc_ref_center has already been subtracted from
2902          * x_ref in init_rot_group.*/
2903         mvmul(erg->rotmat, rotg->x_ref[i], ri);      /* ri  = Omega.(yi0-yc0) */
2904
2905         cprod(rotg->vec, xi_xc, v_xi_xc);            /* v_xi_xc = v x (xi-u)  */
2906
2907         fac = norm2(v_xi_xc);
2908                                           /*                      1           */
2909         psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
2910                                           /*            |v x (xi-xc)|^2 + eps */
2911
2912         psii = gmx_invsqrt(fac);          /*                 1                */
2913                                           /*  psii    = -------------         */
2914                                           /*            |v x (xi-xc)|         */
2915
2916         svmul(psii, v_xi_xc, si);          /*  si = psii * (v x (xi-xc) )     */
2917
2918         fac = iprod(v_xi_xc, ri);                   /* fac = (v x (xi-xc)).ri */
2919         fac2 = fac*fac;
2920
2921         siri = iprod(si, ri);                       /* siri = si.ri           */
2922
2923         svmul(psiistar/psii, ri, tmpvec);
2924         svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
2925         rvec_dec(tmpvec, tmpvec2);
2926         cprod(tmpvec, rotg->vec, tmpvec2);
2927
2928         svmul(wi*siri, tmpvec2, tmpvec);
2929
2930         rvec_inc(sumvec, tmpvec);
2931     }
2932     svmul(rotg->k*erg->invmass, sumvec, innersumvec);
2933 }
2934
2935
2936 /* Calculate the radial motion 2 potential and forces */
2937 static void do_radial_motion2(
2938         t_rotgrp  *rotg,        /* The rotation group                         */
2939         rvec      x[],          /* The positions                              */
2940         matrix    box,          /* The simulation box                         */
2941         double    t,            /* Time in picoseconds                        */
2942         gmx_large_int_t step,   /* The time step                              */
2943         gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
2944         gmx_bool  bOutstepSlab) /* Output per-slab data                       */
2945 {
2946     int       ii,iigrp,ifit,j;
2947     rvec      xj;              /* Position */
2948     real      alpha;           /* a single angle between an actual and a reference position */
2949     real      weight;          /* single weight for a single angle */
2950     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
2951     rvec      xj_u;            /* xj - u */
2952     rvec      yj0_yc0;         /* yj0 -yc0 */
2953     rvec      tmpvec,tmpvec2;
2954     real      fac,fit_fac,fac2,Vpart=0.0;
2955     rvec      rj,fit_rj,sj;
2956     real      sjrj;
2957     rvec      v_xj_u;          /* v x (xj - u) */
2958     real      psij,psijstar;
2959     real      mj,wj;           /* For mass-weighting of the positions */
2960     real      N_M;             /* N/M */
2961     gmx_bool  bPF;
2962     rvec      innersumvec;
2963     gmx_bool  bCalcPotFit;
2964
2965
2966     erg=rotg->enfrotgrp;
2967
2968     bPF = rotg->eType==erotgRM2PF;
2969     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
2970
2971
2972     clear_rvec(yj0_yc0); /* Make the compiler happy */
2973
2974     clear_rvec(innersumvec);
2975     if (bPF)
2976     {
2977         /* For the pivot-free variant we have to use the current center of
2978          * mass of the rotation group instead of the pivot u */
2979         get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
2980
2981         /* Also, we precalculate the second term of the forces that is identical
2982          * (up to the weight factor mj) for all forces */
2983         radial_motion2_precalc_inner_sum(rotg,innersumvec);
2984     }
2985
2986     N_M = rotg->nat * erg->invmass;
2987
2988     /* Each process calculates the forces on its local atoms */
2989     for (j=0; j<erg->nat_loc; j++)
2990     {
2991         if (bPF)
2992         {
2993             /* Local index of a rotation group atom  */
2994             ii = erg->ind_loc[j];
2995             /* Position of this atom in the collective array */
2996             iigrp = erg->xc_ref_ind[j];
2997             /* Mass-weighting */
2998             mj = erg->mc[iigrp];
2999
3000             /* Current position of this atom: x[ii] */
3001             copy_rvec(x[ii], xj);
3002
3003             /* Shift this atom such that it is near its reference */
3004             shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
3005
3006             /* The (unrotated) reference position is yj0. yc0 has already
3007              * been subtracted in init_rot_group */
3008             copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0  */
3009
3010             /* Calculate Omega.(yj0-yc0) */
3011             mvmul(erg->rotmat, yj0_yc0, rj);         /* rj = Omega.(yj0-yc0)  */
3012         }
3013         else
3014         {
3015             mj = erg->m_loc[j];
3016             copy_rvec(erg->x_loc_pbc[j], xj);
3017             copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
3018         }
3019         /* Mass-weighting */
3020         wj = N_M*mj;
3021
3022         /* Calculate (xj-u) resp. (xj-xc) */
3023         rvec_sub(xj, erg->xc_center, xj_u);          /* xj_u = xj-u           */
3024
3025         cprod(rotg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
3026
3027         fac = norm2(v_xj_u);
3028                                           /*                      1           */
3029         psijstar = 1.0/(fac + rotg->eps); /*  psistar = --------------------  */
3030                                           /*            |v x (xj-u)|^2 + eps  */
3031
3032         psij = gmx_invsqrt(fac);          /*                 1                */
3033                                           /*  psij    = ------------          */
3034                                           /*            |v x (xj-u)|          */
3035
3036         svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
3037
3038         fac = iprod(v_xj_u, rj);                     /* fac = (v x (xj-u)).rj */
3039         fac2 = fac*fac;
3040
3041         sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
3042
3043         svmul(psijstar/psij, rj, tmpvec);
3044         svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
3045         rvec_dec(tmpvec, tmpvec2);
3046         cprod(tmpvec, rotg->vec, tmpvec2);
3047
3048         /* Store the additional force so that it can be added to the force
3049          * array after the normal forces have been evaluated */
3050         svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
3051         svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
3052
3053         rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
3054         Vpart += wj*psijstar*fac2;
3055
3056         /* If requested, also calculate the potential for a set of angles
3057          * near the current reference angle */
3058         if (bCalcPotFit)
3059         {
3060             for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
3061             {
3062                 if (bPF)
3063                 {
3064                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
3065                 }
3066                 else
3067                 {
3068                     /* Position of this atom in the collective array */
3069                     iigrp = erg->xc_ref_ind[j];
3070                     /* Rotate with the alternative angle. Like rotate_local_reference(),
3071                      * just for a single local atom */
3072                     mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
3073                 }
3074                 fit_fac = iprod(v_xj_u, fit_rj); /* fac = (v x (xj-u)).fit_rj */
3075                 /* Add to the rotation potential for this angle: */
3076                 erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*psijstar*fit_fac*fit_fac;
3077             }
3078         }
3079
3080         if (bOutstepRot)
3081         {
3082             /* Add to the torque of this rotation group */
3083             erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
3084
3085             /* Calculate the angle between reference and actual rotation group atom. */
3086             angle(rotg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
3087             erg->angle_v  += alpha * weight;
3088             erg->weight_v += weight;
3089         }
3090
3091         PRINT_FORCE_J
3092
3093     } /* end of loop over local rotation group atoms */
3094     erg->V = 0.5*rotg->k*Vpart;
3095 }
3096
3097
3098 /* Determine the smallest and largest position vector (with respect to the 
3099  * rotation vector) for the reference group */
3100 static void get_firstlast_atom_ref(
3101         t_rotgrp  *rotg, 
3102         int       *firstindex, 
3103         int       *lastindex)
3104 {
3105     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
3106     int i;
3107     real xcproj;               /* The projection of a reference position on the 
3108                                   rotation vector */
3109     real minproj, maxproj;     /* Smallest and largest projection on v */
3110     
3111
3112     
3113     erg=rotg->enfrotgrp;
3114
3115     /* Start with some value */
3116     minproj = iprod(rotg->x_ref[0], rotg->vec);
3117     maxproj = minproj;
3118     
3119     /* This is just to ensure that it still works if all the atoms of the 
3120      * reference structure are situated in a plane perpendicular to the rotation 
3121      * vector */
3122     *firstindex = 0;
3123     *lastindex  = rotg->nat-1;
3124     
3125     /* Loop over all atoms of the reference group, 
3126      * project them on the rotation vector to find the extremes */
3127     for (i=0; i<rotg->nat; i++)
3128     {
3129         xcproj = iprod(rotg->x_ref[i], rotg->vec);
3130         if (xcproj < minproj)
3131         {
3132             minproj = xcproj;
3133             *firstindex = i;
3134         }
3135         if (xcproj > maxproj)
3136         {
3137             maxproj = xcproj;
3138             *lastindex = i;
3139         }
3140     }
3141 }
3142
3143
3144 /* Allocate memory for the slabs */
3145 static void allocate_slabs(
3146         t_rotgrp  *rotg, 
3147         FILE      *fplog, 
3148         int       g, 
3149         gmx_bool  bVerbose)
3150 {
3151     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
3152     int i, nslabs;
3153     
3154     
3155     erg=rotg->enfrotgrp;
3156     
3157     /* More slabs than are defined for the reference are never needed */
3158     nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
3159     
3160     /* Remember how many we allocated */
3161     erg->nslabs_alloc = nslabs;
3162
3163     if ( (NULL != fplog) && bVerbose )
3164         fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
3165                 RotStr, nslabs,g);
3166     snew(erg->slab_center     , nslabs);
3167     snew(erg->slab_center_ref , nslabs);
3168     snew(erg->slab_weights    , nslabs);
3169     snew(erg->slab_torque_v   , nslabs);
3170     snew(erg->slab_data       , nslabs);
3171     snew(erg->gn_atom         , nslabs);
3172     snew(erg->gn_slabind      , nslabs);
3173     snew(erg->slab_innersumvec, nslabs);
3174     for (i=0; i<nslabs; i++)
3175     {
3176         snew(erg->slab_data[i].x     , rotg->nat);
3177         snew(erg->slab_data[i].ref   , rotg->nat);
3178         snew(erg->slab_data[i].weight, rotg->nat);
3179     }
3180     snew(erg->xc_ref_sorted, rotg->nat);
3181     snew(erg->xc_sortind   , rotg->nat);
3182     snew(erg->firstatom    , nslabs);
3183     snew(erg->lastatom     , nslabs);
3184 }
3185
3186
3187 /* From the extreme coordinates of the reference group, determine the first 
3188  * and last slab of the reference. We can never have more slabs in the real
3189  * simulation than calculated here for the reference.
3190  */
3191 static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
3192 {
3193     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
3194     int first,last,firststart;
3195     rvec dummy;
3196
3197     
3198     erg=rotg->enfrotgrp;
3199     first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
3200     last  = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
3201     firststart = first;
3202
3203     while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
3204     {
3205         first--;
3206     }
3207     erg->slab_first_ref = first+1;
3208     while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
3209     {
3210         last++;
3211     }
3212     erg->slab_last_ref  = last-1;
3213     
3214     erg->slab_buffer = firststart - erg->slab_first_ref;
3215 }
3216
3217
3218 /* Special version of copy_rvec:
3219  * During the copy procedure of xcurr to b, the correct PBC image is chosen
3220  * such that the copied vector ends up near its reference position xref */
3221 static inline void copy_correct_pbc_image(
3222         const rvec  xcurr,            /* copy vector xcurr ...                */
3223         rvec        b,                /* ... to b ...                         */
3224         const rvec  xref,   /* choosing the PBC image such that b ends up near xref */
3225         matrix      box,
3226         int         npbcdim)
3227 {
3228     rvec  dx;
3229     int   d,m;
3230     ivec  shift;
3231
3232
3233     /* Shortest PBC distance between the atom and its reference */
3234     rvec_sub(xcurr, xref, dx);
3235
3236     /* Determine the shift for this atom */
3237     clear_ivec(shift);
3238     for(m=npbcdim-1; m>=0; m--)
3239     {
3240         while (dx[m] < -0.5*box[m][m])
3241         {
3242             for(d=0; d<DIM; d++)
3243                 dx[d] += box[m][d];
3244             shift[m]++;
3245         }
3246         while (dx[m] >= 0.5*box[m][m])
3247         {
3248             for(d=0; d<DIM; d++)
3249                 dx[d] -= box[m][d];
3250             shift[m]--;
3251         }
3252     }
3253
3254     /* Apply the shift to the position */
3255     copy_rvec(xcurr, b);
3256     shift_single_coord(box, b, shift);
3257 }
3258
3259
3260 static void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
3261         rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs, matrix box,
3262         gmx_bool bOutputCenters)
3263 {
3264     int i,ii;
3265     rvec        coord,*xdum;
3266     gmx_bool    bFlex,bColl;
3267     t_atom      *atom;
3268     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
3269     int         ref_firstindex, ref_lastindex;
3270     gmx_mtop_atomlookup_t alook=NULL;
3271     real        mass,totalmass;
3272     real        start=0.0;
3273     
3274
3275     /* Do we have a flexible axis? */
3276     bFlex = ISFLEX(rotg);
3277     /* Do we use a global set of coordinates? */
3278     bColl = ISCOLL(rotg);
3279
3280     erg=rotg->enfrotgrp;
3281     
3282     /* Allocate space for collective coordinates if needed */
3283     if (bColl)
3284     {
3285         snew(erg->xc        , rotg->nat);
3286         snew(erg->xc_shifts , rotg->nat);
3287         snew(erg->xc_eshifts, rotg->nat);
3288
3289         /* Save the original (whole) set of positions such that later the
3290          * molecule can always be made whole again */
3291         snew(erg->xc_old    , rotg->nat);        
3292         if (MASTER(cr))
3293         {
3294             for (i=0; i<rotg->nat; i++)
3295             {
3296                 ii = rotg->ind[i];
3297                 copy_correct_pbc_image(x[ii], erg->xc_old[i],rotg->x_ref[i],box,3);
3298             }
3299         }
3300 #ifdef GMX_MPI
3301         if (PAR(cr))
3302             gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
3303 #endif
3304
3305         if (rotg->eFittype == erotgFitNORM)
3306         {
3307             snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
3308             snew(erg->xc_norm      , rotg->nat);
3309         }
3310     }
3311     else
3312     {
3313         snew(erg->xr_loc   , rotg->nat);
3314         snew(erg->x_loc_pbc, rotg->nat);
3315     }
3316     
3317     snew(erg->f_rot_loc , rotg->nat);
3318     snew(erg->xc_ref_ind, rotg->nat);
3319     
3320     /* Make space for the calculation of the potential at other angles (used
3321      * for fitting only) */
3322     if (erotgFitPOT == rotg->eFittype)
3323     {
3324         snew(erg->PotAngleFit, 1);
3325         snew(erg->PotAngleFit->degangle, rotg->PotAngle_nstep);
3326         snew(erg->PotAngleFit->V       , rotg->PotAngle_nstep);
3327         snew(erg->PotAngleFit->rotmat  , rotg->PotAngle_nstep);
3328
3329         /* Get the set of angles around the reference angle */
3330         start = -0.5 * (rotg->PotAngle_nstep - 1)*rotg->PotAngle_step;
3331         for (i = 0; i < rotg->PotAngle_nstep; i++)
3332             erg->PotAngleFit->degangle[i] = start + i*rotg->PotAngle_step;
3333     }
3334     else
3335     {
3336         erg->PotAngleFit = NULL;
3337     }
3338
3339     /* xc_ref_ind needs to be set to identity in the serial case */
3340     if (!PAR(cr))
3341         for (i=0; i<rotg->nat; i++)
3342             erg->xc_ref_ind[i] = i;
3343
3344     /* Copy the masses so that the center can be determined. For all types of
3345      * enforced rotation, we store the masses in the erg->mc array. */
3346     if (rotg->bMassW)
3347     {
3348         alook = gmx_mtop_atomlookup_init(mtop);
3349     }
3350     snew(erg->mc, rotg->nat);
3351     if (bFlex)
3352         snew(erg->mc_sorted, rotg->nat);
3353     if (!bColl)
3354         snew(erg->m_loc, rotg->nat);
3355     totalmass=0.0;
3356     for (i=0; i<rotg->nat; i++)
3357     {
3358         if (rotg->bMassW)
3359         {
3360             gmx_mtop_atomnr_to_atom(alook,rotg->ind[i],&atom);
3361             mass=atom->m;
3362         }
3363         else
3364         {
3365             mass=1.0;
3366         }
3367         erg->mc[i] = mass;
3368         totalmass += mass;
3369     }
3370     erg->invmass = 1.0/totalmass;
3371     
3372     if (rotg->bMassW)
3373     {
3374         gmx_mtop_atomlookup_destroy(alook);
3375     }
3376
3377     /* Set xc_ref_center for any rotation potential */
3378     if ((rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2))
3379     {
3380         /* Set the pivot point for the fixed, stationary-axis potentials. This
3381          * won't change during the simulation */
3382         copy_rvec(rotg->pivot, erg->xc_ref_center);
3383         copy_rvec(rotg->pivot, erg->xc_center    );
3384     }
3385     else
3386     {
3387         /* Center of the reference positions */
3388         get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
3389
3390         /* Center of the actual positions */
3391         if (MASTER(cr))
3392         {
3393             snew(xdum, rotg->nat);
3394             for (i=0; i<rotg->nat; i++)
3395             {
3396                 ii = rotg->ind[i];
3397                 copy_rvec(x[ii], xdum[i]);
3398             }
3399             get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
3400             sfree(xdum);
3401         }
3402 #ifdef GMX_MPI
3403         if (PAR(cr))
3404             gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
3405 #endif
3406     }
3407
3408     if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
3409     {
3410         /* Put the reference positions into origin: */
3411         for (i=0; i<rotg->nat; i++)
3412             rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
3413     }
3414
3415     /* Enforced rotation with flexible axis */
3416     if (bFlex)
3417     {
3418         /* Calculate maximum beta value from minimum gaussian (performance opt.) */
3419         erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
3420
3421         /* Determine the smallest and largest coordinate with respect to the rotation vector */
3422         get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
3423         
3424         /* From the extreme coordinates of the reference group, determine the first 
3425          * and last slab of the reference. */
3426         get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
3427                 
3428         /* Allocate memory for the slabs */
3429         allocate_slabs(rotg, fplog, g, bVerbose);
3430
3431         /* Flexible rotation: determine the reference centers for the rest of the simulation */
3432         erg->slab_first = erg->slab_first_ref;
3433         erg->slab_last = erg->slab_last_ref;
3434         get_slab_centers(rotg,rotg->x_ref,erg->mc,g,-1,out_slabs,bOutputCenters,TRUE);
3435
3436         /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
3437         if (rotg->eFittype == erotgFitNORM)
3438         {
3439             for (i=0; i<rotg->nat; i++)
3440             {
3441                 rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
3442                 erg->xc_ref_length[i] = norm(coord);
3443             }
3444         }
3445     }
3446 }
3447
3448
3449 extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot)
3450 {
3451     gmx_ga2la_t ga2la;
3452     int g;
3453     t_rotgrp *rotg;
3454     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
3455     
3456     ga2la = dd->ga2la;
3457
3458     for(g=0; g<rot->ngrp; g++)
3459     {
3460         rotg = &rot->grp[g];
3461         erg  = rotg->enfrotgrp;
3462
3463
3464         dd_make_local_group_indices(ga2la,rotg->nat,rotg->ind,
3465                 &erg->nat_loc,&erg->ind_loc,&erg->nalloc_loc,erg->xc_ref_ind);
3466     }
3467 }
3468
3469
3470 /* Calculate the size of the MPI buffer needed in reduce_output() */
3471 static int calc_mpi_bufsize(t_rot *rot)
3472 {
3473     int g;
3474     int count_group, count_total;
3475     t_rotgrp *rotg;
3476     gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
3477
3478
3479     count_total = 0;
3480     for (g=0; g<rot->ngrp; g++)
3481     {
3482         rotg = &rot->grp[g];
3483         erg  = rotg->enfrotgrp;
3484
3485         /* Count the items that are transferred for this group: */
3486         count_group = 4; /* V, torque, angle, weight */
3487
3488         /* Add the maximum number of slabs for flexible groups */
3489         if (ISFLEX(rotg))
3490             count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
3491
3492         /* Add space for the potentials at different angles: */
3493         if (erotgFitPOT == rotg->eFittype)
3494             count_group += rotg->PotAngle_nstep;
3495
3496         /* Add to the total number: */
3497         count_total += count_group;
3498     }
3499
3500     return count_total;
3501 }
3502
3503
3504 extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
3505         t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
3506         gmx_bool bVerbose, unsigned long Flags)
3507 {
3508     t_rot    *rot;
3509     t_rotgrp *rotg;
3510     int      g;
3511     int      nat_max=0;     /* Size of biggest rotation group */
3512     gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
3513     gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
3514     rvec     *x_pbc=NULL;   /* Space for the pbc-correct atom positions */
3515
3516
3517     if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
3518         gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
3519
3520     if ( MASTER(cr) && bVerbose)
3521         fprintf(stdout, "%s Initializing ...\n", RotStr);
3522
3523     rot = ir->rot;
3524     snew(rot->enfrot, 1);
3525     er = rot->enfrot;
3526     er->Flags = Flags;
3527
3528     /* When appending, skip first output to avoid duplicate entries in the data files */
3529     if (er->Flags & MD_APPENDFILES)
3530         er->bOut = FALSE;
3531     else
3532         er->bOut = TRUE;
3533
3534     if ( MASTER(cr) && er->bOut )
3535         please_cite(fplog, "Kutzner2011");
3536
3537     /* Output every step for reruns */
3538     if (er->Flags & MD_RERUN)
3539     {
3540         if (NULL != fplog)
3541             fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
3542         rot->nstrout = 1;
3543         rot->nstsout = 1;
3544     }
3545
3546     er->out_slabs = NULL;
3547     if ( MASTER(cr) && HaveFlexibleGroups(rot) )
3548         er->out_slabs = open_slab_out(opt2fn("-rs",nfile,fnm), rot, oenv);
3549
3550     if (MASTER(cr))
3551     {
3552         /* Remove pbc, make molecule whole.
3553          * When ir->bContinuation=TRUE this has already been done, but ok. */
3554         snew(x_pbc,mtop->natoms);
3555         m_rveccopy(mtop->natoms,x,x_pbc);
3556         do_pbc_first_mtop(NULL,ir->ePBC,box,mtop,x_pbc);
3557         /* All molecules will be whole now, but not necessarily in the home box.
3558          * Additionally, if a rotation group consists of more than one molecule
3559          * (e.g. two strands of DNA), each one of them can end up in a different
3560          * periodic box. This is taken care of in init_rot_group.  */
3561     }
3562
3563     for (g=0; g<rot->ngrp; g++)
3564     {
3565         rotg = &rot->grp[g];
3566
3567         if (NULL != fplog)
3568             fprintf(fplog,"%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
3569         
3570         if (rotg->nat > 0)
3571         {
3572             /* Allocate space for the rotation group's data: */
3573             snew(rotg->enfrotgrp, 1);
3574             erg  = rotg->enfrotgrp;
3575
3576             nat_max=max(nat_max, rotg->nat);
3577             
3578             if (PAR(cr))
3579             {
3580                 erg->nat_loc    = 0;
3581                 erg->nalloc_loc = 0;
3582                 erg->ind_loc    = NULL;
3583             }
3584             else
3585             {
3586                 erg->nat_loc = rotg->nat;
3587                 erg->ind_loc = rotg->ind;
3588             }
3589             init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs,box,
3590                            !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
3591                                                              * again if we are appending */
3592         }
3593     }
3594     
3595     /* Allocate space for enforced rotation buffer variables */
3596     er->bufsize = nat_max;
3597     snew(er->data, nat_max);
3598     snew(er->xbuf, nat_max);
3599     snew(er->mbuf, nat_max);
3600
3601     /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
3602     if (PAR(cr))
3603     {
3604         er->mpi_bufsize = calc_mpi_bufsize(rot) + 100; /* larger to catch errors */
3605         snew(er->mpi_inbuf , er->mpi_bufsize);
3606         snew(er->mpi_outbuf, er->mpi_bufsize);
3607     }
3608     else
3609     {
3610         er->mpi_bufsize = 0;
3611         er->mpi_inbuf = NULL;
3612         er->mpi_outbuf = NULL;
3613     }
3614
3615     /* Only do I/O on the MASTER */
3616     er->out_angles  = NULL;
3617     er->out_rot     = NULL;
3618     er->out_torque  = NULL;
3619     if (MASTER(cr))
3620     {
3621         er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv);
3622
3623         if (rot->nstsout > 0)
3624         {
3625             if ( HaveFlexibleGroups(rot) || HavePotFitGroups(rot) )
3626                 er->out_angles  = open_angles_out(opt2fn("-ra",nfile,fnm), rot, oenv);
3627             if ( HaveFlexibleGroups(rot) )
3628                 er->out_torque  = open_torque_out(opt2fn("-rt",nfile,fnm), rot, oenv);
3629         }
3630
3631         sfree(x_pbc);
3632     }
3633 }
3634
3635
3636 extern void finish_rot(FILE *fplog,t_rot *rot)
3637 {
3638     gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
3639
3640     
3641     er=rot->enfrot;
3642     if (er->out_rot)
3643         gmx_fio_fclose(er->out_rot);
3644     if (er->out_slabs)
3645         gmx_fio_fclose(er->out_slabs);
3646     if (er->out_angles)
3647         gmx_fio_fclose(er->out_angles);
3648     if (er->out_torque)
3649         gmx_fio_fclose(er->out_torque);
3650 }
3651
3652
3653 /* Rotate the local reference positions and store them in
3654  * erg->xr_loc[0...(nat_loc-1)]
3655  *
3656  * Note that we already subtracted u or y_c from the reference positions
3657  * in init_rot_group().
3658  */
3659 static void rotate_local_reference(t_rotgrp *rotg)
3660 {
3661     gmx_enfrotgrp_t erg;
3662     int i,ii;
3663
3664     
3665     erg=rotg->enfrotgrp;
3666     
3667     for (i=0; i<erg->nat_loc; i++)
3668     {
3669         /* Index of this rotation group atom with respect to the whole rotation group */
3670         ii = erg->xc_ref_ind[i];
3671         /* Rotate */
3672         mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
3673     }
3674 }
3675
3676
3677 /* Select the PBC representation for each local x position and store that
3678  * for later usage. We assume the right PBC image of an x is the one nearest to
3679  * its rotated reference */
3680 static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
3681 {
3682     int i,ii;
3683     gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
3684     rvec xref;
3685
3686
3687     erg=rotg->enfrotgrp;
3688
3689     for (i=0; i<erg->nat_loc; i++)
3690     {
3691         /* Index of a rotation group atom  */
3692         ii = erg->ind_loc[i];
3693
3694         /* Get the reference position. The pivot was already
3695          * subtracted in init_rot_group() from the reference positions. Also,
3696          * the reference positions have already been rotated in
3697          * rotate_local_reference() */
3698         copy_rvec(erg->xr_loc[i], xref);
3699
3700         copy_correct_pbc_image(x[ii],erg->x_loc_pbc[i], xref, box, npbcdim);
3701     }
3702 }
3703
3704
3705 extern void do_rotation(
3706         t_commrec *cr,
3707         t_inputrec *ir,
3708         matrix box,
3709         rvec x[],
3710         real t,
3711         gmx_large_int_t step,
3712         gmx_wallcycle_t wcycle,
3713         gmx_bool bNS)
3714 {
3715     int      g,i,ii;
3716     t_rot    *rot;
3717     t_rotgrp *rotg;
3718     gmx_bool outstep_slab, outstep_rot;
3719     gmx_bool bFlex,bColl;
3720     gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
3721     gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
3722     rvec     transvec;
3723     t_gmx_potfit *fit=NULL;     /* For fit type 'potential' determine the fit
3724                                    angle via the potential minimum            */
3725
3726     /* Enforced rotation cycle counting: */
3727     gmx_cycles_t cycles_comp;   /* Cycles for the enf. rotation computation
3728                                    only, does not count communication. This
3729                                    counter is used for load-balancing         */
3730
3731 #ifdef TAKETIME
3732     double t0;
3733 #endif
3734     
3735     rot=ir->rot;
3736     er=rot->enfrot;
3737     
3738     /* When to output in main rotation output file */
3739     outstep_rot  = do_per_step(step, rot->nstrout) && er->bOut;
3740     /* When to output per-slab data */
3741     outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
3742
3743     /* Output time into rotation output file */
3744     if (outstep_rot && MASTER(cr))
3745         fprintf(er->out_rot, "%12.3e",t);
3746
3747     /**************************************************************************/
3748     /* First do ALL the communication! */
3749     for(g=0; g<rot->ngrp; g++)
3750     {
3751         rotg = &rot->grp[g];
3752         erg=rotg->enfrotgrp;
3753
3754         /* Do we have a flexible axis? */
3755         bFlex = ISFLEX(rotg);
3756         /* Do we use a collective (global) set of coordinates? */
3757         bColl = ISCOLL(rotg);
3758
3759         /* Calculate the rotation matrix for this angle: */
3760         erg->degangle = rotg->rate * t;
3761         calc_rotmat(rotg->vec,erg->degangle,erg->rotmat);
3762
3763         if (bColl)
3764         {
3765             /* Transfer the rotation group's positions such that every node has
3766              * all of them. Every node contributes its local positions x and stores
3767              * it in the collective erg->xc array. */
3768             communicate_group_positions(cr,erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
3769                     x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
3770         }
3771         else
3772         {
3773             /* Fill the local masses array;
3774              * this array changes in DD/neighborsearching steps */
3775             if (bNS)
3776             {
3777                 for (i=0; i<erg->nat_loc; i++)
3778                 {
3779                     /* Index of local atom w.r.t. the collective rotation group */
3780                     ii = erg->xc_ref_ind[i];
3781                     erg->m_loc[i] = erg->mc[ii];
3782                 }
3783             }
3784
3785             /* Calculate Omega*(y_i-y_c) for the local positions */
3786             rotate_local_reference(rotg);
3787
3788             /* Choose the nearest PBC images of the group atoms with respect
3789              * to the rotated reference positions */
3790             choose_pbc_image(x, rotg, box, 3);
3791
3792             /* Get the center of the rotation group */
3793             if ( (rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) )
3794                 get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
3795         }
3796
3797     } /* End of loop over rotation groups */
3798
3799     /**************************************************************************/
3800     /* Done communicating, we can start to count cycles for the load balancing now ... */
3801     cycles_comp = gmx_cycles_read();
3802
3803
3804 #ifdef TAKETIME
3805     t0 = MPI_Wtime();
3806 #endif
3807
3808     for(g=0; g<rot->ngrp; g++)
3809     {
3810         rotg = &rot->grp[g];
3811         erg=rotg->enfrotgrp;
3812
3813         bFlex = ISFLEX(rotg);
3814         bColl = ISCOLL(rotg);
3815
3816         if (outstep_rot && MASTER(cr))
3817             fprintf(er->out_rot, "%12.4f", erg->degangle);
3818
3819         /* Calculate angles and rotation matrices for potential fitting: */
3820         if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
3821         {
3822             fit = erg->PotAngleFit;
3823             for (i = 0; i < rotg->PotAngle_nstep; i++)
3824             {
3825                 calc_rotmat(rotg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
3826
3827                 /* Clear value from last step */
3828                 erg->PotAngleFit->V[i] = 0.0;
3829             }
3830         }
3831
3832         /* Clear values from last time step */
3833         erg->V        = 0.0;
3834         erg->torque_v = 0.0;
3835         erg->angle_v  = 0.0;
3836         erg->weight_v = 0.0;
3837
3838         switch(rotg->eType)
3839         {
3840             case erotgISO:
3841             case erotgISOPF:
3842             case erotgPM:
3843             case erotgPMPF:
3844                 do_fixed(rotg,x,box,t,step,outstep_rot,outstep_slab);
3845                 break;
3846             case erotgRM:
3847                 do_radial_motion(rotg,x,box,t,step,outstep_rot,outstep_slab);
3848                 break;
3849             case erotgRMPF:
3850                 do_radial_motion_pf(rotg,x,box,t,step,outstep_rot,outstep_slab);
3851                 break;
3852             case erotgRM2:
3853             case erotgRM2PF:
3854                 do_radial_motion2(rotg,x,box,t,step,outstep_rot,outstep_slab);
3855                 break;
3856             case erotgFLEXT:
3857             case erotgFLEX2T:
3858                 /* Subtract the center of the rotation group from the collective positions array
3859                  * Also store the center in erg->xc_center since it needs to be subtracted
3860                  * in the low level routines from the local coordinates as well */
3861                 get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
3862                 svmul(-1.0, erg->xc_center, transvec);
3863                 translate_x(erg->xc, rotg->nat, transvec);
3864                 do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
3865                 break;
3866             case erotgFLEX:
3867             case erotgFLEX2:
3868                 /* Do NOT subtract the center of mass in the low level routines! */
3869                 clear_rvec(erg->xc_center);
3870                 do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
3871                 break;
3872             default:
3873                 gmx_fatal(FARGS, "No such rotation potential.");
3874                 break;
3875         }
3876     }
3877
3878 #ifdef TAKETIME
3879     if (MASTER(cr))
3880         fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
3881 #endif
3882
3883     /* Stop the enforced rotation cycle counter and add the computation-only
3884      * cycles to the force cycles for load balancing */
3885     cycles_comp  = gmx_cycles_read() - cycles_comp;
3886
3887     if (DOMAINDECOMP(cr) && wcycle)
3888         dd_cycles_add(cr->dd,cycles_comp,ddCyclF);
3889 }