Enforced rotation
authorCarsten Kutzner <ckutzne@gwdg.de>
Wed, 23 Nov 2011 17:37:38 +0000 (18:37 +0100)
committerCarsten Kutzner <ckutzne@gwdg.de>
Mon, 19 Dec 2011 13:09:27 +0000 (14:09 +0100)
Change-Id: Ia2d3b409c197fd2a6bd5922b636bd40a47046918

24 files changed:
include/gmx_wallcycle.h
include/maths.h
include/names.h
include/pull_rotation.h [new file with mode: 0644]
include/types/enums.h
include/types/inputrec.h
src/gmxlib/copyrite.c
src/gmxlib/mvdata.c
src/gmxlib/names.c
src/gmxlib/tpxio.c
src/gmxlib/txtdump.c
src/kernel/CMakeLists.txt
src/kernel/grompp.c
src/kernel/mdrun.c
src/kernel/readir.c
src/kernel/readir.h
src/kernel/readrot.c [new file with mode: 0644]
src/kernel/runner.c
src/mdlib/domdec.c
src/mdlib/gmx_wallcycle.c
src/mdlib/mdebin.c
src/mdlib/pull_rotation.c [new file with mode: 0644]
src/mdlib/sim_util.c
src/tools/gmx_tune_pme.c

index 9d5c5cf23bddedbdb7fffd7697de98bd3b490d4d..afaeb0d6985869ef41d519faf864895bbf71656b 100644 (file)
@@ -43,7 +43,7 @@
 extern "C" {
 #endif
 
 extern "C" {
 #endif
 
-  enum { ewcRUN, ewcSTEP, ewcPPDURINGPME, ewcDOMDEC, ewcDDCOMMLOAD, ewcDDCOMMBOUND, ewcVSITECONSTR, ewcPP_PMESENDX, ewcMOVEX, ewcNS, ewcGB, ewcFORCE, ewcMOVEF, ewcPMEMESH, ewcPME_REDISTXF, ewcPME_SPREADGATHER, ewcPME_FFT, ewcPME_SOLVE, ewcPMEWAITCOMM, ewcPP_PMEWAITRECVF, ewcVSITESPREAD, ewcTRAJ, ewcUPDATE, ewcCONSTR, ewcMoveE, ewcTEST, ewcNR };
+  enum { ewcRUN, ewcSTEP, ewcPPDURINGPME, ewcDOMDEC, ewcDDCOMMLOAD, ewcDDCOMMBOUND, ewcVSITECONSTR, ewcPP_PMESENDX, ewcMOVEX, ewcNS, ewcGB, ewcFORCE, ewcMOVEF, ewcPMEMESH, ewcPME_REDISTXF, ewcPME_SPREADGATHER, ewcPME_FFT, ewcPME_SOLVE, ewcPMEWAITCOMM, ewcPP_PMEWAITRECVF, ewcVSITESPREAD, ewcTRAJ, ewcUPDATE, ewcCONSTR, ewcMoveE, ewcROT, ewcROTadd, ewcTEST, ewcNR };
 
 gmx_bool wallcycle_have_counter(void);
 /* Returns if cycle counting is supported */
 
 gmx_bool wallcycle_have_counter(void);
 /* Returns if cycle counting is supported */
index 782e7688f10b6c7c03b5a8191840aea99f710365..f4a998e042d09bc93244b7aad043018dc5e06c30 100644 (file)
@@ -60,6 +60,10 @@ extern "C" {
 #ifndef M_SQRT2
 #define M_SQRT2 sqrt(2.0)
 #endif
 #ifndef M_SQRT2
 #define M_SQRT2 sqrt(2.0)
 #endif
+
+#ifndef M_1_PI
+#define M_1_PI      0.31830988618379067154
+#endif
     
 /* Suzuki-Yoshida Constants, for n=3 and n=5, for symplectic integration  */
 /* for n=1, w0 = 1 */
     
 /* Suzuki-Yoshida Constants, for n=3 and n=5, for symplectic integration  */
 /* for n=1, w0 = 1 */
index a396bdae528985121d18174b644d12bb2439ddc2..bd00ec531cd87aea4a1a15a355e45eefa7a0aed5 100644 (file)
@@ -82,6 +82,9 @@ extern const char *esa_names[esaNR+1];
 extern const char *ewt_names[ewtNR+1];
 extern const char *epull_names[epullNR+1];
 extern const char *epullg_names[epullgNR+1];
 extern const char *ewt_names[ewtNR+1];
 extern const char *epull_names[epullNR+1];
 extern const char *epullg_names[epullgNR+1];
+extern const char *erotg_names[erotgNR+1];
+extern const char *erotg_originnames[erotgNR+1];
+extern const char *erotg_fitnames[erotgFitNR+1];
 extern const char *eQMmethod_names[eQMmethodNR+1];
 extern const char *eQMbasis_names[eQMbasisNR+1];
 extern const char *eQMMMscheme_names[eQMMMschemeNR+1];
 extern const char *eQMmethod_names[eQMmethodNR+1];
 extern const char *eQMbasis_names[eQMbasisNR+1];
 extern const char *eQMMMscheme_names[eQMMMschemeNR+1];
@@ -121,6 +124,9 @@ extern const char *eMultentOpt_names[eMultentOptNR+1];
 #define EWALLTYPE(e)   ENUM_NAME(e,ewtNR,ewt_names)
 #define EPULLTYPE(e)   ENUM_NAME(e,epullNR,epull_names)
 #define EPULLGEOM(e)   ENUM_NAME(e,epullgNR,epullg_names)
 #define EWALLTYPE(e)   ENUM_NAME(e,ewtNR,ewt_names)
 #define EPULLTYPE(e)   ENUM_NAME(e,epullNR,epull_names)
 #define EPULLGEOM(e)   ENUM_NAME(e,epullgNR,epullg_names)
+#define EROTGEOM(e)    ENUM_NAME(e,erotgNR,erotg_names)
+#define EROTORIGIN(e)  ENUM_NAME(e,erotgOriginNR,erotg_originnames)
+#define EROTFIT(e)     ENUM_NAME(e,erotgFitNR,erotg_fitnames)
 #define EQMMETHOD(e)   ENUM_NAME(e,eQMmethodNR,eQMmethod_names)
 #define EQMBASIS(e)    ENUM_NAME(e,eQMbasisNR,eQMbasis_names)
 #define EQMMMSCHEME(e) ENUM_NAME(e,eQMMMschemeNR,eQMMMscheme_names)
 #define EQMMETHOD(e)   ENUM_NAME(e,eQMmethodNR,eQMmethod_names)
 #define EQMBASIS(e)    ENUM_NAME(e,eQMbasisNR,eQMbasis_names)
 #define EQMMMSCHEME(e) ENUM_NAME(e,eQMMMschemeNR,eQMMMscheme_names)
diff --git a/include/pull_rotation.h b/include/pull_rotation.h
new file mode 100644 (file)
index 0000000..8de318e
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 
+ *                This source code is part of
+ * 
+ *                 G   R   O   M   A   C   S
+ * 
+ *          GROningen MAchine for Chemical Simulations
+ * 
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ * 
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ * 
+ * For more info, check our website at http://www.gromacs.org
+ * 
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+/*! \file pull_rotation.h
+ *
+ *  @brief Enforced rotation of protein parts or other groups of particles.
+ *
+ *  This file contains routines that are used to enforce rotational motion
+ *  upon a subgroup of particles.  
+ *  
+ */
+
+#ifndef _pull_rotation_h
+#define _pull_rotation_h
+
+#ifdef HAVE_CONFIG_H
+  #include <config.h>
+#endif
+
+#include "vec.h"
+#include "typedefs.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*! \brief Initialize the enforced rotation groups.
+ * 
+ * This routine does the memory allocation for various helper arrays, opens
+ * the output files etc.  
+ *
+ * \param fplog             General output file, normally md.log.
+ * \param ir                Struct containing MD input parameters, among those
+ *                          also the enforced rotation parameters.
+ * \param nfile             Number of entries in the fnm structure.       
+ * \param fnm               The filenames struct containing also the names
+ *                          of the rotation output files.
+ * \param cr                Pointer to MPI communication data.
+ * \param x                 The positions of all MD particles.
+ * \param mtop              Molecular topology.
+ * \param oenv              Needed to open the rotation output xvgr file.
+ * \param Flags             Flags passed over from main, used to determine
+ *                          whether or not we are doing a rerun.
+ */
+extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
+        t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+        gmx_bool bVerbose, unsigned long Flags);
+
+
+/*! \brief Make a selection of the home atoms for all enforced rotation groups.
+ *
+ * This routine is similar to dd_make_local_pull_groups, but works only with
+ * domain decomposition. It should be called at every domain decomposition.
+ *
+ * \param dd                Structure containing domain decomposition data.
+ * \param rot               Pointer to all the enforced rotation data.
+ */
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot);
+
+
+/*! \brief Calculation of the enforced rotation potential.
+ * 
+ * This is the main enforced rotation module which is called during every time
+ * step. Here the rotation potential as well as the resulting forces are 
+ * calculated.
+ * 
+ * \param cr                Pointer to MPI communication data.
+ * \param ir                Struct containing MD input parameters, among those
+ * \param box               Simulation box, needed to make group whole.
+ * \param x                 The positions of all the local particles.
+ * \param t                 Time.
+ * \param step              The time step.
+ * \param wcycle            During the potential calculation the wallcycles are
+ *                          counted. Later they enter the dynamic load balancing.
+ * \param bNS               After domain decomposition / neighborsearching several
+ *                          local arrays have to be updated (masses, shifts)
+ */
+extern void do_rotation(t_commrec *cr,t_inputrec *ir,matrix box,rvec x[],real t,
+        gmx_large_int_t step,gmx_wallcycle_t wcycle,gmx_bool bNS);
+
+
+/*! \brief Add the enforced rotation forces to the official force array.
+ * 
+ * Adds the forces from enforced rotation potential to the local forces and
+ * sums up the contributions to the rotation potential from all the nodes. Since
+ * this needs communication, this routine should be called after the SR forces 
+ * have been evaluated (in order not to spoil cycle counts). 
+ * This routine also outputs data to the various rotation output files (e.g.
+ * the potential, the angle of the group, torques and more).
+ * 
+ * \param rot               Pointer to all the enforced rotation data.
+ * \param f                 The local forces to which the rotational forces have
+ *                          to be added.
+ * \param cr                Pointer to MPI communication data.
+ * \param step              The time step, used for output.
+ * \param t                 Time, used for output.
+ */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t);
+
+
+/*! \brief Close the enforced rotation output files.
+ *
+ * \param fplog             General output file, normally md.log.
+ * \param rot               Pointer to all the enforced rotation data.
+ */
+extern void finish_rot(FILE *fplog,t_rot *rot);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
index a12c3074446a701ca5f5a4842da98bf8efb0361e..a7b9227c15bae66ed265c6360f1ba6ff15e499d2 100644 (file)
@@ -224,6 +224,21 @@ enum {
 
 #define PULL_CYL(pull) ((pull)->eGeom == epullgCYL)
 
 
 #define PULL_CYL(pull) ((pull)->eGeom == epullgCYL)
 
+/* Enforced rotation groups */
+enum {
+  erotgISO  , erotgISOPF ,
+  erotgPM   , erotgPMPF  ,
+  erotgRM   , erotgRMPF  ,
+  erotgRM2  , erotgRM2PF ,
+  erotgFLEX , erotgFLEXT ,
+  erotgFLEX2, erotgFLEX2T,
+  erotgNR
+};
+
+enum {
+    erotgFitRMSD, erotgFitNORM, erotgFitPOT, erotgFitNR
+};
+
 /* QMMM */
 enum {
   eQMmethodAM1, eQMmethodPM3, eQMmethodRHF, 
 /* QMMM */
 enum {
   eQMmethodAM1, eQMmethodPM3, eQMmethodRHF, 
index 7c993638fc930bdde4e56413cdfc3e86db57563c..bf8566d39e89f810d2b9c99282d43092b9fb44fc 100644 (file)
@@ -145,6 +145,44 @@ typedef struct {
   FILE       *out_f;      /* output file for pull data */
 } t_pull;
 
   FILE       *out_f;      /* output file for pull data */
 } t_pull;
 
+
+/* Abstract types for enforced rotation only defined in pull_rotation.c       */
+typedef struct gmx_enfrot *gmx_enfrot_t;
+typedef struct gmx_enfrotgrp *gmx_enfrotgrp_t;
+
+typedef struct {
+  int        eType;          /* Rotation type for this group                  */
+  int        bMassW;         /* Use mass-weighed positions?                   */
+  int        nat;            /* Number of atoms in the group                  */
+  atom_id    *ind;           /* The global atoms numbers                      */
+  rvec       *x_ref;         /* The reference positions                       */
+  rvec       vec;            /* The normalized rotation vector                */
+  real       rate;           /* Rate of rotation (degree/ps)                  */
+  real       k;              /* Force constant (kJ/(mol nm^2)                 */
+  rvec       pivot;          /* Pivot point of rotation axis (nm)             */
+  int        eFittype;       /* Type of fit to determine actual group angle   */
+  int        PotAngle_nstep; /* Number of angles around the reference angle
+                                for which the rotation potential is also
+                                evaluated (for fit type 'potential' only)     */
+  real       PotAngle_step;  /* Distance between two angles in degrees (for
+                                fit type 'potential' only)                    */
+  real       slab_dist;      /* Slab distance (nm)                            */
+  real       min_gaussian;   /* Minimum value the gaussian must have so that 
+                                the force is actually evaluated               */
+  real       eps;            /* Additive constant for radial motion2 and
+                                flexible2 potentials (nm^2)                   */
+  gmx_enfrotgrp_t enfrotgrp; /* Stores non-inputrec rotation data per group   */
+} t_rotgrp;
+
+typedef struct {
+  int        ngrp;           /* Number of rotation groups                     */
+  int        nstrout;        /* Output frequency for main rotation outfile    */
+  int        nstsout;        /* Output frequency for per-slab data            */
+  t_rotgrp   *grp;           /* Groups to rotate                              */
+  gmx_enfrot_t enfrot;       /* Stores non-inputrec enforced rotation data    */
+} t_rot;
+
+
 typedef struct {
   int  eI;              /* Integration method                          */
   gmx_large_int_t nsteps;      /* number of steps to be taken                  */
 typedef struct {
   int  eI;              /* Integration method                          */
   gmx_large_int_t nsteps;      /* number of steps to be taken                  */
@@ -267,6 +305,8 @@ typedef struct {
   real wall_ewald_zfac; /* Scaling factor for the box for Ewald         */
   int  ePull;           /* Type of pulling: no, umbrella or constraint  */
   t_pull *pull;         /* The data for center of mass pulling          */
   real wall_ewald_zfac; /* Scaling factor for the box for Ewald         */
   int  ePull;           /* Type of pulling: no, umbrella or constraint  */
   t_pull *pull;         /* The data for center of mass pulling          */
+  gmx_bool bRot;        /* Calculate enforced rotation potential(s)?    */
+  t_rot *rot;           /* The data for enforced rotation potentials    */
   real cos_accel;       /* Acceleration for viscosity calculation       */
   tensor deform;        /* Triclinic deformation velocities (nm/ps)     */
   int  userint1;        /* User determined parameters                   */
   real cos_accel;       /* Acceleration for viscosity calculation       */
   tensor deform;        /* Triclinic deformation velocities (nm/ps)     */
   int  userint1;        /* User determined parameters                   */
index 48267b802710165b097675bea391acb608ec0deb..8dc0ae0a4c5d22f88b49616dc8896b324430441e 100644 (file)
@@ -527,8 +527,12 @@ void please_cite(FILE *fp,const char *key)
       "H. Wang, F. Dommert, C.Holm",
       "Optimizing working parameters of the smooth particle mesh Ewald algorithm in terms of accuracy and efficiency",
       "J. Chem. Phys. B",
       "H. Wang, F. Dommert, C.Holm",
       "Optimizing working parameters of the smooth particle mesh Ewald algorithm in terms of accuracy and efficiency",
       "J. Chem. Phys. B",
-      133, 2010, "034117"
-    }
+      133, 2010, "034117" },
+    { "Kutzner2011",
+      "C. Kutzner and J. Czub and H. Grubmuller",
+      "Keep it Flexible: Driving Macromolecular Rotary Motions in Atomistic Simulations with GROMACS",
+      "J. Chem. Theory Comput.",
+      7, 2011, "1381-1393" }
   };
 #define NSTR (int)asize(citedb)
   
   };
 #define NSTR (int)asize(citedb)
   
index e25baaf08808e3c0b66c4306b9aef6938487f8e2..5b2df261062a78de70a854c2b7940b69c5cfbafa 100644 (file)
@@ -461,6 +461,27 @@ static void bc_pull(const t_commrec *cr,t_pull *pull)
   }
 }
 
   }
 }
 
+static void bc_rotgrp(const t_commrec *cr,t_rotgrp *rotg)
+{
+  block_bc(cr,*rotg);
+  if (rotg->nat > 0) {
+    snew_bc(cr,rotg->ind,rotg->nat);
+    nblock_bc(cr,rotg->nat,rotg->ind);
+    snew_bc(cr,rotg->x_ref,rotg->nat);
+    nblock_bc(cr,rotg->nat,rotg->x_ref);
+  }
+}
+
+static void bc_rot(const t_commrec *cr,t_rot *rot)
+{
+  int g;
+
+  block_bc(cr,*rot);
+  snew_bc(cr,rot->grp,rot->ngrp);
+  for(g=0; g<rot->ngrp; g++)
+    bc_rotgrp(cr,&rot->grp[g]);
+}
+
 static void bc_inputrec(const t_commrec *cr,t_inputrec *inputrec)
 {
   gmx_bool bAlloc=TRUE;
 static void bc_inputrec(const t_commrec *cr,t_inputrec *inputrec)
 {
   gmx_bool bAlloc=TRUE;
@@ -474,6 +495,10 @@ static void bc_inputrec(const t_commrec *cr,t_inputrec *inputrec)
     snew_bc(cr,inputrec->pull,1);
     bc_pull(cr,inputrec->pull);
   }
     snew_bc(cr,inputrec->pull,1);
     bc_pull(cr,inputrec->pull);
   }
+  if (inputrec->bRot) {
+    snew_bc(cr,inputrec->rot,1);
+    bc_rot(cr,inputrec->rot);
+  }
   for(i=0; (i<DIM); i++) {
     bc_cosines(cr,&(inputrec->ex[i]));
     bc_cosines(cr,&(inputrec->et[i]));
   for(i=0; (i<DIM); i++) {
     bc_cosines(cr,&(inputrec->ex[i]));
     bc_cosines(cr,&(inputrec->et[i]));
index 9f9ad810af810eb234eb1c88ef15d42224dd8f2c..470a0c17c909e8534b336fd04a9bc29c48c62eff 100644 (file)
@@ -188,6 +188,14 @@ const char *epullg_names[epullgNR+1] = {
   "distance", "direction", "cylinder", "position", "direction-periodic", NULL
 };
 
   "distance", "direction", "cylinder", "position", "direction-periodic", NULL
 };
 
+const char *erotg_names[erotgNR+1] = { 
+  "iso", "iso-pf", "pm", "pm-pf", "rm", "rm-pf", "rm2", "rm2-pf", "flex", "flex-t", "flex2", "flex2-t", NULL
+};
+
+const char *erotg_fitnames[erotgFitNR+1] = { 
+  "rmsd", "norm", "potential", NULL
+};
+
 const char *eQMmethod_names[eQMmethodNR+1] = {
   "AM1", "PM3", "RHF",
   "UHF", "DFT", "B3LYP", "MP2", "CASSCF","B3LYPLAN",
 const char *eQMmethod_names[eQMmethodNR+1] = {
   "AM1", "PM3", "RHF",
   "UHF", "DFT", "B3LYP", "MP2", "CASSCF","B3LYPLAN",
index fd30a89e0dc0c2c3e6b9a63b5b6f15d35528b21a..240ca992595eefd6ce5319f369427996ba80a3eb 100644 (file)
@@ -64,7 +64,7 @@
 #include "mtop_util.h"
 
 /* This number should be increased whenever the file format changes! */
 #include "mtop_util.h"
 
 /* This number should be increased whenever the file format changes! */
-static const int tpx_version = 73;
+static const int tpx_version = 74;
 
 /* This number should only be increased when you edit the TOPOLOGY section
  * of the tpx format. This way we can maintain forward compatibility too
 
 /* This number should only be increased when you edit the TOPOLOGY section
  * of the tpx format. This way we can maintain forward compatibility too
@@ -249,6 +249,47 @@ static void do_pull(t_fileio *fio, t_pull *pull,gmx_bool bRead, int file_version
     do_pullgrp(fio,&pull->grp[g],bRead,file_version);
 }
 
     do_pullgrp(fio,&pull->grp[g],bRead,file_version);
 }
 
+
+static void do_rotgrp(t_fileio *fio, t_rotgrp *rotg,gmx_bool bRead, int file_version)
+{
+  gmx_bool bDum=TRUE;
+  int  i;
+
+  gmx_fio_do_int(fio,rotg->eType);
+  gmx_fio_do_int(fio,rotg->bMassW);
+  gmx_fio_do_int(fio,rotg->nat);
+  if (bRead)
+    snew(rotg->ind,rotg->nat);
+  gmx_fio_ndo_int(fio,rotg->ind,rotg->nat);
+  if (bRead)
+      snew(rotg->x_ref,rotg->nat);
+  gmx_fio_ndo_rvec(fio,rotg->x_ref,rotg->nat);
+  gmx_fio_do_rvec(fio,rotg->vec);
+  gmx_fio_do_rvec(fio,rotg->pivot);
+  gmx_fio_do_real(fio,rotg->rate);
+  gmx_fio_do_real(fio,rotg->k);
+  gmx_fio_do_real(fio,rotg->slab_dist);
+  gmx_fio_do_real(fio,rotg->min_gaussian);
+  gmx_fio_do_real(fio,rotg->eps);
+  gmx_fio_do_int(fio,rotg->eFittype);
+  gmx_fio_do_int(fio,rotg->PotAngle_nstep);
+  gmx_fio_do_real(fio,rotg->PotAngle_step);
+}
+
+static void do_rot(t_fileio *fio, t_rot *rot,gmx_bool bRead, int file_version)
+{
+  int g;
+
+  gmx_fio_do_int(fio,rot->ngrp);
+  gmx_fio_do_int(fio,rot->nstrout);
+  gmx_fio_do_int(fio,rot->nstsout);
+  if (bRead)
+    snew(rot->grp,rot->ngrp);
+  for(g=0; g<rot->ngrp; g++)
+    do_rotgrp(fio, &rot->grp[g],bRead,file_version);
+}
+
+
 static void do_inputrec(t_fileio *fio, t_inputrec *ir,gmx_bool bRead, 
                         int file_version, real *fudgeQQ)
 {
 static void do_inputrec(t_fileio *fio, t_inputrec *ir,gmx_bool bRead, 
                         int file_version, real *fudgeQQ)
 {
@@ -757,6 +798,18 @@ static void do_inputrec(t_fileio *fio, t_inputrec *ir,gmx_bool bRead,
       ir->ePull = epullNO;
     }
     
       ir->ePull = epullNO;
     }
     
+    /* Enforced rotation */
+    if (file_version >= 74) {
+        gmx_fio_do_int(fio,ir->bRot);
+        if (ir->bRot == TRUE) {
+            if (bRead)
+                snew(ir->rot,1);
+            do_rot(fio, ir->rot,bRead,file_version);
+        }
+    } else {
+        ir->bRot = FALSE;
+    }
+    
     /* grpopts stuff */
     gmx_fio_do_int(fio,ir->opts.ngtc); 
     if (file_version >= 69) {
     /* grpopts stuff */
     gmx_fio_do_int(fio,ir->opts.ngtc); 
     if (file_version >= 69) {
index 0221e2f36fed35ffbe2f101942971d14d0a1acfc..33c98378c7ab52821bf3a7e1bb53e5acee2d3e2e 100644 (file)
@@ -508,6 +508,38 @@ static void pr_pull(FILE *fp,int indent,t_pull *pull)
     pr_pullgrp(fp,indent,g,&pull->grp[g]);
 }
 
     pr_pullgrp(fp,indent,g,&pull->grp[g]);
 }
 
+static void pr_rotgrp(FILE *fp,int indent,int g,t_rotgrp *rotg)
+{
+  pr_indent(fp,indent);
+  fprintf(fp,"rotation_group %d:\n",g);
+  indent += 2;
+  PS("type",EROTGEOM(rotg->eType));
+  PS("massw",BOOL(rotg->bMassW));
+  pr_ivec_block(fp,indent,"atom",rotg->ind,rotg->nat,TRUE);
+  pr_rvecs(fp,indent,"x_ref",rotg->x_ref,rotg->nat);
+  pr_rvec(fp,indent,"vec",rotg->vec,DIM,TRUE);
+  pr_rvec(fp,indent,"pivot",rotg->pivot,DIM,TRUE);
+  PR("rate",rotg->rate);
+  PR("k",rotg->k);
+  PR("slab_dist",rotg->slab_dist);
+  PR("min_gaussian",rotg->min_gaussian);
+  PR("epsilon",rotg->eps);
+  PS("fit_method",EROTFIT(rotg->eFittype));
+  PI("potfitangle_nstep",rotg->PotAngle_nstep);
+  PR("potfitangle_step",rotg->PotAngle_step);
+}
+
+static void pr_rot(FILE *fp,int indent,t_rot *rot)
+{
+  int g;
+
+  PI("rot_nstrout",rot->nstrout);
+  PI("rot_nstsout",rot->nstsout);
+  PI("rot_ngrp",rot->ngrp);
+  for(g=0; g<rot->ngrp; g++)
+    pr_rotgrp(fp,indent,g,&rot->grp[g]);
+}
+
 void pr_inputrec(FILE *fp,int indent,const char *title,t_inputrec *ir,
                  gmx_bool bMDPformat)
 {
 void pr_inputrec(FILE *fp,int indent,const char *title,t_inputrec *ir,
                  gmx_bool bMDPformat)
 {
@@ -638,6 +670,10 @@ void pr_inputrec(FILE *fp,int indent,const char *title,t_inputrec *ir,
     PS("pull",EPULLTYPE(ir->ePull));
     if (ir->ePull != epullNO)
       pr_pull(fp,indent,ir->pull);
     PS("pull",EPULLTYPE(ir->ePull));
     if (ir->ePull != epullNO)
       pr_pull(fp,indent,ir->pull);
+    
+    PS("rotation",BOOL(ir->bRot));
+    if (ir->bRot)
+      pr_rot(fp,indent,ir->rot);
 
     PS("disre",EDISRETYPE(ir->eDisre));
     PS("disre-weighting",EDISREWEIGHTING(ir->eDisreWeighting));
 
     PS("disre",EDISRETYPE(ir->eDisre));
     PS("disre-weighting",EDISREWEIGHTING(ir->eDisreWeighting));
index 1e6cf9656bd92aeb85994c8feb53e0c1981721ec..f35457ce843a0ba98a2a15426ae7b1291b85ad20 100644 (file)
@@ -16,6 +16,7 @@ set(GMXPREPROCESS_SOURCES
     pgutil.c        
     readir.c        
     readpull.c      
     pgutil.c        
     readir.c        
     readpull.c      
+    readrot.c
     resall.c        
     sorting.c       
     specbond.c      
     resall.c        
     sorting.c       
     specbond.c      
index 2d3b71663653feb38cef25828305a4186060286d..19c181fb3c2d6eb836a478b3105033698d17b28c 100644 (file)
@@ -1263,7 +1263,8 @@ int main (int argc, char *argv[])
     { efTOP, "-pp", "processed", ffOPTWR },
     { efTPX, "-o",  NULL,        ffWRITE },
     { efTRN, "-t",  NULL,        ffOPTRD },
     { efTOP, "-pp", "processed", ffOPTWR },
     { efTPX, "-o",  NULL,        ffWRITE },
     { efTRN, "-t",  NULL,        ffOPTRD },
-    { efEDR, "-e",  NULL,        ffOPTRD }
+    { efEDR, "-e",  NULL,        ffOPTRD },
+    { efTRN, "-ref","rotref",    ffOPTRW }
   };
 #define NFILE asize(fnm)
 
   };
 #define NFILE asize(fnm)
 
@@ -1547,6 +1548,13 @@ int main (int argc, char *argv[])
 
   if (ir->ePull != epullNO)
     set_pull_init(ir,sys,state.x,state.box,oenv,opts->pull_start);
 
   if (ir->ePull != epullNO)
     set_pull_init(ir,sys,state.x,state.box,oenv,opts->pull_start);
+  
+  if (ir->bRot)
+  {
+      set_reference_positions(ir->rot,sys,state.x,state.box,
+                              opt2fn("-ref",NFILE,fnm),opt2bSet("-ref",NFILE,fnm),
+                              wi);
+  }
 
   /*  reset_multinr(sys); */
   
 
   /*  reset_multinr(sys); */
   
index 1a6021a4fe5877883fe2ca21b2ddb863854413ce..eab7081316c4d803dd13a5c02597f545f5b7571a 100644 (file)
@@ -376,6 +376,10 @@ int main(int argc,char *argv[])
     { efXVG, "-runav",  "runaver",  ffOPTWR },
     { efXVG, "-px",     "pullx",    ffOPTWR },
     { efXVG, "-pf",     "pullf",    ffOPTWR },
     { efXVG, "-runav",  "runaver",  ffOPTWR },
     { efXVG, "-px",     "pullx",    ffOPTWR },
     { efXVG, "-pf",     "pullf",    ffOPTWR },
+    { efXVG, "-ro",     "rotation", ffOPTWR },
+    { efLOG, "-ra",     "rotangles",ffOPTWR },
+    { efLOG, "-rs",     "rotslabs", ffOPTWR },
+    { efLOG, "-rt",     "rottorque",ffOPTWR },
     { efMTX, "-mtx",    "nm",       ffOPTWR },
     { efNDX, "-dn",     "dipole",   ffOPTWR },
     { efRND, "-multidir",NULL,      ffOPTRDMULT}
     { efMTX, "-mtx",    "nm",       ffOPTWR },
     { efNDX, "-dn",     "dipole",   ffOPTWR },
     { efRND, "-multidir",NULL,      ffOPTRDMULT}
index 50f6a8a75307e4831bd24242e4c3f4775cea673f..97f207861623f178609fa1c12eb3a28593aac4af 100644 (file)
@@ -79,6 +79,7 @@ static char tcgrps[STRLEN],tau_t[STRLEN],ref_t[STRLEN],
   wall_atomtype[STRLEN],wall_density[STRLEN],deform[STRLEN],QMMM[STRLEN];
 static char foreign_lambda[STRLEN];
 static char **pull_grp;
   wall_atomtype[STRLEN],wall_density[STRLEN],deform[STRLEN],QMMM[STRLEN];
 static char foreign_lambda[STRLEN];
 static char **pull_grp;
+static char **rot_grp;
 static char anneal[STRLEN],anneal_npoints[STRLEN],
   anneal_time[STRLEN],anneal_temp[STRLEN];
 static char QMmethod[STRLEN],QMbasis[STRLEN],QMcharge[STRLEN],QMmult[STRLEN],
 static char anneal[STRLEN],anneal_npoints[STRLEN],
   anneal_time[STRLEN],anneal_temp[STRLEN];
 static char QMmethod[STRLEN],QMbasis[STRLEN],QMcharge[STRLEN],QMmult[STRLEN],
@@ -1081,6 +1082,15 @@ void get_ir(const char *mdparin,const char *mdparout,
     snew(ir->pull,1);
     pull_grp = read_pullparams(&ninp,&inp,ir->pull,&opts->pull_start,wi);
   }
     snew(ir->pull,1);
     pull_grp = read_pullparams(&ninp,&inp,ir->pull,&opts->pull_start,wi);
   }
+  
+  /* Enforced rotation */
+  CCTYPE("ENFORCED ROTATION");
+  CTYPE("Enforced rotation: No or Yes");
+  EETYPE("rotation",       ir->bRot, yesno_names);
+  if (ir->bRot) {
+    snew(ir->rot,1);
+    rot_grp = read_rotparams(&ninp,&inp,ir->rot,wi);
+  }
 
   /* Refinement */
   CCTYPE("NMR refinement stuff");
 
   /* Refinement */
   CCTYPE("NMR refinement stuff");
@@ -1999,6 +2009,10 @@ void do_index(const char* mdparin, const char *ndx,
   if (ir->ePull != epullNO) {
     make_pull_groups(ir->pull,pull_grp,grps,gnames);
   }
   if (ir->ePull != epullNO) {
     make_pull_groups(ir->pull,pull_grp,grps,gnames);
   }
+  
+  if (ir->bRot) {
+    make_rotation_groups(ir->rot,rot_grp,grps,gnames);
+  }
 
   nacc = str_nelem(acc,MAXPTR,ptr1);
   nacg = str_nelem(accgrps,MAXPTR,ptr2);
 
   nacc = str_nelem(acc,MAXPTR,ptr1);
   nacg = str_nelem(accgrps,MAXPTR,ptr2);
index d9a18e4f7426880327cf846c62d36bb795e0eb08..3f99910756986cca0381b202dd243f1c84a47303 100644 (file)
@@ -136,4 +136,14 @@ extern void set_pull_init(t_inputrec *ir,gmx_mtop_t *mtop,rvec *x,matrix box,
  * If bStart adds the distance to the initial reference location.
  */
 
  * If bStart adds the distance to the initial reference location.
  */
 
+extern char **read_rotparams(int *ninp_p,t_inpfile **inp,t_rot *rot,warninp_t wi);
+/* Reads enforced rotation parameters, returns a list of the rot group names */
+
+extern void make_rotation_groups(t_rot *rot,char **rotgnames,
+                 t_blocka *grps,char **gnames);
+/* Process the rotation parameters after reading the index groups */
+
+extern void set_reference_positions(t_rot *rot, gmx_mtop_t *mtop, rvec *x, matrix box,
+        const char *fn, gmx_bool bSet, warninp_t wi);
+
 #endif /* _readir_h */
 #endif /* _readir_h */
diff --git a/src/kernel/readrot.c b/src/kernel/readrot.c
new file mode 100644 (file)
index 0000000..a660188
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ *                This source code is part of
+ * 
+ *                 G   R   O   M   A   C   S
+ * 
+ *          GROningen MAchine for Chemical Simulations
+ * 
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ * 
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ * 
+ * For more info, check our website at http://www.gromacs.org
+ * 
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "vec.h"
+#include "smalloc.h"
+#include "readir.h"
+#include "names.h"
+#include "futil.h"
+#include "trnio.h"
+#include "txtdump.h"
+
+static char *RotStr = {"Enforced rotation:"};
+
+
+static char s_vec[STRLEN];
+
+
+static void string2dvec(char buf[], dvec nums)
+{
+    if (sscanf(buf,"%lf%lf%lf",&nums[0],&nums[1],&nums[2]) != 3)
+        gmx_fatal(FARGS,"Expected three numbers at input line %s",buf);
+}
+
+
+extern char **read_rotparams(int *ninp_p,t_inpfile **inp_p,t_rot *rot,
+        warninp_t wi)
+{
+    int  ninp,g,m;
+    t_inpfile *inp;
+    const char *tmp;
+    char **grpbuf;
+    char buf[STRLEN];
+    char warn_buf[STRLEN];
+    dvec vec;
+    t_rotgrp *rotg;
+
+    ninp   = *ninp_p;
+    inp    = *inp_p;
+    
+    /* read rotation parameters */
+    CTYPE("Output frequency for angle, torque and rotation potential energy for the whole group");
+    ITYPE("rot_nstrout",     rot->nstrout, 100);
+    CTYPE("Output frequency for per-slab data (angles, torques and slab centers)");
+    ITYPE("rot_nstsout",     rot->nstsout, 1000);
+    CTYPE("Number of rotation groups");
+    ITYPE("rot_ngroups",     rot->ngrp,1);
+    
+    if (rot->ngrp < 1)
+    {
+        gmx_fatal(FARGS,"rot_ngroups should be >= 1");
+    }
+    
+    snew(rot->grp,rot->ngrp);
+    
+    /* Read the rotation groups */
+    snew(grpbuf,rot->ngrp);
+    for(g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        snew(grpbuf[g],STRLEN);
+        CTYPE("Rotation group name");
+        sprintf(buf,"rot_group%d",g);
+        STYPE(buf, grpbuf[g], "");
+        
+        CTYPE("Rotation potential. Can be iso, iso-pf, pm, pm-pf, rm, rm-pf, rm2, rm2-pf, flex, flex-t, flex2, flex2-t");
+        sprintf(buf,"rot_type%d",g);
+        ETYPE(buf, rotg->eType, erotg_names);
+
+        CTYPE("Use mass-weighting of the rotation group positions");
+        sprintf(buf,"rot_massw%d",g);
+        ETYPE(buf, rotg->bMassW, yesno_names);
+
+        CTYPE("Rotation vector, will get normalized");
+        sprintf(buf,"rot_vec%d",g);
+        STYPE(buf, s_vec, "1.0 0.0 0.0");
+        string2dvec(s_vec,vec);
+        /* Normalize the rotation vector */
+        if (dnorm(vec) != 0)
+        {
+            dsvmul(1.0/dnorm(vec),vec,vec);
+        }
+        else
+        {
+            sprintf(warn_buf, "rot_vec%d = 0", g);
+            warning_error(wi, warn_buf);
+        }
+        fprintf(stderr, "%s Group %d (%s) normalized rot. vector: %f %f %f\n",
+                RotStr, g, erotg_names[rotg->eType], vec[0], vec[1], vec[2]);
+        for(m=0; m<DIM; m++)
+            rotg->vec[m] = vec[m];
+        
+        CTYPE("Pivot point for the potentials iso, pm, rm, and rm2 (nm)");
+        sprintf(buf,"rot_pivot%d",g);
+        STYPE(buf, s_vec, "0.0 0.0 0.0");
+        clear_dvec(vec);
+        if ( (rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2) )
+            string2dvec(s_vec,vec);
+        for(m=0; m<DIM; m++)
+            rotg->pivot[m] = vec[m];
+
+        CTYPE("Rotation rate (degree/ps) and force constant (kJ/(mol*nm^2))");
+        sprintf(buf,"rot_rate%d",g);
+        RTYPE(buf, rotg->rate, 0.0);
+
+        sprintf(buf,"rot_k%d",g);
+        RTYPE(buf, rotg->k, 0.0);
+        if (rotg->k <= 0.0)
+        {
+            sprintf(warn_buf, "rot_k%d <= 0", g);
+            warning_note(wi, warn_buf);
+        }
+
+        CTYPE("Slab distance for flexible axis rotation (nm)");
+        sprintf(buf,"rot_slab_dist%d",g);
+        RTYPE(buf, rotg->slab_dist, 1.5);
+        if (rotg->slab_dist <= 0.0)
+        {
+            sprintf(warn_buf, "rot_slab_dist%d <= 0", g);
+            warning_error(wi, warn_buf);
+        }
+
+        CTYPE("Minimum value of Gaussian function for the force to be evaluated (for flex* potentials)");
+        sprintf(buf,"rot_min_gauss%d",g);
+        RTYPE(buf, rotg->min_gaussian, 1e-3);
+        if (rotg->min_gaussian <= 0.0)
+        {
+            sprintf(warn_buf, "rot_min_gauss%d <= 0", g);
+            warning_error(wi, warn_buf);
+        }
+
+        CTYPE("Value of additive constant epsilon' (nm^2) for rm2* and flex2* potentials");
+        sprintf(buf, "rot_eps%d",g);
+        RTYPE(buf, rotg->eps, 1e-4);
+        if ( (rotg->eps <= 0.0) && (rotg->eType==erotgRM2 || rotg->eType==erotgFLEX2) )
+        {
+            sprintf(warn_buf, "rot_eps%d <= 0", g);
+            warning_error(wi, warn_buf);
+        }
+
+        CTYPE("Fitting method to determine angle of rotation group (rmsd, norm, or potential)");
+        sprintf(buf,"rot_fit_method%d",g);
+        ETYPE(buf, rotg->eFittype, erotg_fitnames);
+        CTYPE("For fit type 'potential', nr. of angles around the reference for which the pot. is evaluated");
+        sprintf(buf,"rot_potfit_nsteps%d",g);
+        ITYPE(buf, rotg->PotAngle_nstep, 21);
+        if ( (rotg->eFittype==erotgFitPOT) && (rotg->PotAngle_nstep < 1) )
+        {
+            sprintf(warn_buf, "rot_potfit_nsteps%d < 1", g);
+            warning_error(wi, warn_buf);
+        }
+        CTYPE("For fit type 'potential', distance in degrees between two consecutive angles");
+        sprintf(buf,"rot_potfit_step%d",g);
+        RTYPE(buf, rotg->PotAngle_step, 0.25);
+    }
+    
+    *ninp_p   = ninp;
+    *inp_p    = inp;
+    
+    return grpbuf;
+}
+
+
+/* Check whether the box is unchanged */
+static void check_box(matrix f_box, matrix box, char fn[], warninp_t wi)
+{
+    int i,ii;
+    gmx_bool bSame=TRUE;
+    char warn_buf[STRLEN];
+    
+    
+    for (i=0; i<DIM; i++)
+        for (ii=0; ii<DIM; ii++)
+            if (f_box[i][ii] != box[i][ii]) 
+                bSame = FALSE;
+    if (!bSame)
+    {
+        sprintf(warn_buf, "%s Box size in reference file %s differs from actual box size!",
+                RotStr, fn);
+        warning(wi, warn_buf);
+        pr_rvecs(stderr,0,"Your box is:",box  ,3);
+        pr_rvecs(stderr,0,"Box in file:",f_box,3);
+    }
+}
+
+
+/* Extract the reference positions for the rotation group(s) */
+extern void set_reference_positions(
+        t_rot *rot, gmx_mtop_t *mtop, rvec *x, matrix box,
+        const char *fn, gmx_bool bSet, warninp_t wi)
+{
+    int g,i,ii;
+    t_rotgrp *rotg;
+    t_trnheader header;    /* Header information of reference file */
+    char base[STRLEN],extension[STRLEN],reffile[STRLEN];
+    char *extpos;
+    rvec f_box[3];         /* Box from reference file */
+
+    
+    /* Base name and extension of the reference file: */
+    strncpy(base, fn, STRLEN - 1);
+    extpos = strrchr(base, '.');
+    strcpy(extension,extpos+1);
+    *extpos = '\0';
+
+
+    for (g=0; g<rot->ngrp; g++)
+     {
+         rotg = &rot->grp[g];
+         fprintf(stderr, "%s group %d has %d reference positions.\n",RotStr,g,rotg->nat);
+         snew(rotg->x_ref, rotg->nat);
+         
+         /* Construct the name for the file containing the reference positions for this group: */
+         sprintf(reffile, "%s.%d.%s", base,g,extension);
+
+         /* If the base filename for the reference position files was explicitly set by
+          * the user, we issue a fatal error if the group file can not be found */
+         if (bSet && !gmx_fexist(reffile))
+         {
+             gmx_fatal(FARGS, "%s The file containing the reference positions was not found.\n"
+                              "Expected the file '%s' for group %d.\n",
+                              RotStr, reffile, g);
+         }
+
+         if (gmx_fexist(reffile))
+         {
+             fprintf(stderr, "  Reading them from %s.\n", reffile);
+             read_trnheader(reffile, &header);
+             if (rotg->nat != header.natoms)
+                 gmx_fatal(FARGS,"Number of atoms in file %s (%d) does not match the number of atoms in rotation group (%d)!\n",
+                         reffile, header.natoms, rotg->nat);
+             read_trn(reffile, &header.step, &header.t, &header.lambda, f_box, &header.natoms, rotg->x_ref, NULL, NULL);
+
+             /* Check whether the box is unchanged and output a warning if not: */
+             check_box(f_box,box,reffile,wi);
+         }
+         else
+         {
+             fprintf(stderr, " Saving them to %s.\n", reffile);         
+             for(i=0; i<rotg->nat; i++)
+             {
+                 ii = rotg->ind[i];
+                 copy_rvec(x[ii], rotg->x_ref[i]);
+             }
+             write_trn(reffile,g,0.0,0.0,box,rotg->nat,rotg->x_ref,NULL,NULL);
+         }
+     }
+}
+
+
+extern void make_rotation_groups(t_rot *rot,char **rotgnames,t_blocka *grps,char **gnames)
+{
+    int      g,ig=-1,i;
+    t_rotgrp *rotg;
+    
+    
+    for (g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        ig = search_string(rotgnames[g],grps->nr,gnames);
+        rotg->nat = grps->index[ig+1] - grps->index[ig];
+        
+        if (rotg->nat > 0)
+        {
+            fprintf(stderr,"Rotation group %d '%s' has %d atoms\n",g,rotgnames[g],rotg->nat);
+            snew(rotg->ind,rotg->nat);
+            for(i=0; i<rotg->nat; i++)
+                rotg->ind[i] = grps->a[grps->index[ig]+i];            
+        }
+        else
+            gmx_fatal(FARGS,"Rotation group %d '%s' is empty",g,rotgnames[g]);
+    }
+}
index 0db72cb64d489d469c8d1d344ef683baf4d99d44..2c46054852bbf5a18c4aeb0d97947c0060c9379b 100644 (file)
@@ -72,7 +72,7 @@
 #include "sighandler.h"
 #include "tpxio.h"
 #include "txtdump.h"
 #include "sighandler.h"
 #include "tpxio.h"
 #include "txtdump.h"
-
+#include "pull_rotation.h"
 #include "md_openmm.h"
 
 #ifdef GMX_LIB_MPI
 #include "md_openmm.h"
 
 #ifdef GMX_LIB_MPI
@@ -794,6 +794,13 @@ int mdrunner(int nthreads_requested, FILE *fplog,t_commrec *cr,int nfile,
             init_pull(fplog,inputrec,nfile,fnm,mtop,cr,oenv,
                       EI_DYNAMICS(inputrec->eI) && MASTER(cr),Flags);
         }
             init_pull(fplog,inputrec,nfile,fnm,mtop,cr,oenv,
                       EI_DYNAMICS(inputrec->eI) && MASTER(cr),Flags);
         }
+        
+        if (inputrec->bRot)
+        {
+           /* Initialize enforced rotation code */
+           init_rot(fplog,inputrec,nfile,fnm,cr,state->x,state->box,mtop,oenv,
+                    bVerbose,Flags);
+        }
 
         constr = init_constraints(fplog,mtop,inputrec,ed,state,cr);
 
 
         constr = init_constraints(fplog,mtop,inputrec,ed,state,cr);
 
@@ -825,6 +832,12 @@ int mdrunner(int nthreads_requested, FILE *fplog,t_commrec *cr,int nfile,
         {
             finish_pull(fplog,inputrec->pull);
         }
         {
             finish_pull(fplog,inputrec->pull);
         }
+        
+        if (inputrec->bRot)
+        {
+            finish_rot(fplog,inputrec->rot);
+        }
+
     } 
     else 
     {
     } 
     else 
     {
index abed24fd224a3d9d4e818ef2d49db8cb2dbe12ea..ec9775d4fcd504624e5bcfa035b8c71865292507 100644 (file)
@@ -41,6 +41,7 @@
 #include "force.h"
 #include "pme.h"
 #include "pull.h"
 #include "force.h"
 #include "pme.h"
 #include "pull.h"
+#include "pull_rotation.h"
 #include "gmx_wallcycle.h"
 #include "mdrun.h"
 #include "nsgrid.h"
 #include "gmx_wallcycle.h"
 #include "mdrun.h"
 #include "nsgrid.h"
@@ -8602,6 +8603,13 @@ void dd_partition_system(FILE            *fplog,
         /* Update the local pull groups */
         dd_make_local_pull_groups(dd,ir->pull,mdatoms);
     }
         /* Update the local pull groups */
         dd_make_local_pull_groups(dd,ir->pull,mdatoms);
     }
+    
+    if (ir->bRot)
+    {
+        /* Update the local rotation groups */
+        dd_make_local_rotation_groups(dd,ir->rot);
+    }
+
 
     add_dd_statistics(dd);
     
 
     add_dd_statistics(dd);
     
index 8224baf55951dddc4149648b9808faca3afff9c7..af3fa95996ec80ecafc5af4a8574f95aab83414e 100644 (file)
@@ -76,7 +76,7 @@ typedef struct gmx_wallcycle
 
 /* Each name should not exceed 19 characters */
 static const char *wcn[ewcNR] =
 
 /* Each name should not exceed 19 characters */
 static const char *wcn[ewcNR] =
-{ "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load", "DD comm. bounds", "Vsite constr.", "Send X to PME", "Comm. coord.", "Neighbor search", "Born radii", "Force", "Wait + Comm. F", "PME mesh", "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME solve", "Wait + Comm. X/F", "Wait + Recv. PME F", "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies", "Test" };
+{ "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load", "DD comm. bounds", "Vsite constr.", "Send X to PME", "Comm. coord.", "Neighbor search", "Born radii", "Force", "Wait + Comm. F", "PME mesh", "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME solve", "Wait + Comm. X/F", "Wait + Recv. PME F", "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies", "Enforced rotation", "Add rot. forces", "Test" };
 
 gmx_bool wallcycle_have_counter(void)
 {
 
 gmx_bool wallcycle_have_counter(void)
 {
index 7e102e132e80cecdb66718c4f39cd6d1323e5386..204a1305c99ad232d72fdb5b0fd42f5e71048913 100644 (file)
@@ -229,7 +229,7 @@ t_mdebin *init_mdebin(ener_file_t fp_ene,
         else if (i == F_CONNBONDS)
             md->bEner[i] = FALSE;
         else if (i == F_COM_PULL)
         else if (i == F_CONNBONDS)
             md->bEner[i] = FALSE;
         else if (i == F_COM_PULL)
-            md->bEner[i] = (ir->ePull == epullUMBRELLA || ir->ePull == epullCONST_F);
+            md->bEner[i] = (ir->ePull == epullUMBRELLA || ir->ePull == epullCONST_F || ir->bRot);
         else if (i == F_ECONSERVED)
             md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
                             (ir->epc == epcNO || ir->epc==epcMTTK));
         else if (i == F_ECONSERVED)
             md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
                             (ir->epc == epcNO || ir->epc==epcMTTK));
diff --git a/src/mdlib/pull_rotation.c b/src/mdlib/pull_rotation.c
new file mode 100644 (file)
index 0000000..060d471
--- /dev/null
@@ -0,0 +1,3858 @@
+/*
+ * 
+ *                This source code is part of
+ * 
+ *                 G   R   O   M   A   C   S
+ * 
+ *          GROningen MAchine for Chemical Simulations
+ * 
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ * 
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ * 
+ * For more info, check our website at http://www.gromacs.org
+ * 
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "domdec.h"
+#include "gmx_wallcycle.h"
+#include "trnio.h"
+#include "smalloc.h"
+#include "network.h"
+#include "pbc.h"
+#include "futil.h"
+#include "mdrun.h"
+#include "txtdump.h"
+#include "names.h"
+#include "mtop_util.h"
+#include "names.h"
+#include "nrjac.h"
+#include "vec.h"
+#include "gmx_ga2la.h"
+#include "xvgr.h"
+#include "gmxfio.h"
+#include "groupcoord.h"
+#include "pull_rotation.h"
+#include "gmx_sort.h"
+#include "copyrite.h"
+#include "gmx_cyclecounter.h"
+
+
+static char *RotStr = {"Enforced rotation:"};
+
+
+/* Set the minimum weight for the determination of the slab centers */
+#define WEIGHT_MIN (10*GMX_FLOAT_MIN)
+
+/* Helper structure for sorting positions along rotation vector             */
+typedef struct {
+    real xcproj;            /* Projection of xc on the rotation vector        */
+    int ind;                /* Index of xc                                    */
+    real m;                 /* Mass                                           */
+    rvec x;                 /* Position                                       */
+    rvec x_ref;             /* Reference position                             */
+} sort_along_vec_t;
+
+
+/* Enforced rotation / flexible: determine the angle of each slab             */
+typedef struct gmx_slabdata
+{
+    int  nat;               /* Number of atoms belonging to this slab         */
+    rvec *x;                /* The positions belonging to this slab. In 
+                               general, this should be all positions of the 
+                               whole rotation group, but we leave those away 
+                               that have a small enough weight                */
+    rvec *ref;              /* Same for reference                             */
+    real *weight;           /* The weight for each atom                       */
+} t_gmx_slabdata;
+
+
+/* Helper structure for potential fitting */
+typedef struct gmx_potfit
+{
+    real   *degangle;       /* Set of angles for which the potential is
+                               calculated. The optimum fit is determined as
+                               the angle for with the potential is minimal    */
+    real   *V;              /* Potential for the different angles             */
+    matrix *rotmat;         /* Rotation matrix corresponding to the angles    */
+} t_gmx_potfit;
+
+
+/* Enforced rotation data for all groups                                      */
+typedef struct gmx_enfrot
+{
+    FILE  *out_rot;         /* Output file for rotation data                  */
+    FILE  *out_torque;      /* Output file for torque data                    */
+    FILE  *out_angles;      /* Output file for slab angles for flexible type  */
+    FILE  *out_slabs;       /* Output file for slab centers                   */
+    int   bufsize;          /* Allocation size of buf                         */
+    rvec  *xbuf;            /* Coordinate buffer variable for sorting         */
+    real  *mbuf;            /* Masses buffer variable for sorting             */
+    sort_along_vec_t *data; /* Buffer variable needed for position sorting    */
+    real  *mpi_inbuf;       /* MPI buffer                                     */
+    real  *mpi_outbuf;      /* MPI buffer                                     */
+    int   mpi_bufsize;      /* Allocation size of in & outbuf                 */
+    unsigned long Flags;    /* mdrun flags                                    */
+    gmx_bool bOut;          /* Used to skip first output when appending to 
+                             * avoid duplicate entries in rotation outfiles   */
+} t_gmx_enfrot;
+
+
+/* Global enforced rotation data for a single rotation group                  */
+typedef struct gmx_enfrotgrp
+{
+    real    degangle;       /* Rotation angle in degrees                      */
+    matrix  rotmat;         /* Rotation matrix                                */
+    atom_id *ind_loc;       /* Local rotation indices                         */
+    int     nat_loc;        /* Number of local group atoms                    */
+    int     nalloc_loc;     /* Allocation size for ind_loc and weight_loc     */
+
+    real  V;                /* Rotation potential for this rotation group     */
+    rvec  *f_rot_loc;       /* Array to store the forces on the local atoms
+                               resulting from enforced rotation potential     */
+
+    /* Collective coordinates for the whole rotation group */
+    real  *xc_ref_length;   /* Length of each x_rotref vector after x_rotref 
+                               has been put into origin                       */
+    int   *xc_ref_ind;      /* Position of each local atom in the collective
+                               array                                          */
+    rvec  xc_center;        /* Center of the rotation group positions, may
+                               be mass weighted                               */
+    rvec  xc_ref_center;    /* dito, for the reference positions              */
+    rvec  *xc;              /* Current (collective) positions                 */
+    ivec  *xc_shifts;       /* Current (collective) shifts                    */
+    ivec  *xc_eshifts;      /* Extra shifts since last DD step                */
+    rvec  *xc_old;          /* Old (collective) positions                     */
+    rvec  *xc_norm;         /* Normalized form of the current positions       */
+    rvec  *xc_ref_sorted;   /* Reference positions (sorted in the same order 
+                               as xc when sorted)                             */
+    int   *xc_sortind;      /* Where is a position found after sorting?       */
+    real  *mc;              /* Collective masses                              */
+    real  *mc_sorted;
+    real  invmass;          /* one over the total mass of the rotation group  */
+
+    real  torque_v;         /* Torque in the direction of rotation vector     */
+    real  angle_v;          /* Actual angle of the whole rotation group       */
+    /* Fixed rotation only */
+    real  weight_v;         /* Weights for angle determination                */
+    rvec  *xr_loc;          /* Local reference coords, correctly rotated      */
+    rvec  *x_loc_pbc;       /* Local current coords, correct PBC image        */
+    real  *m_loc;           /* Masses of the current local atoms              */
+
+    /* Flexible rotation only */
+    int   nslabs_alloc;     /* For this many slabs memory is allocated        */
+    int   slab_first;       /* Lowermost slab for that the calculation needs 
+                               to be performed at a given time step           */
+    int   slab_last;        /* Uppermost slab ...                             */
+    int   slab_first_ref;   /* First slab for which ref. center is stored     */
+    int   slab_last_ref;    /* Last ...                                       */
+    int   slab_buffer;      /* Slab buffer region around reference slabs      */
+    int   *firstatom;       /* First relevant atom for a slab                 */
+    int   *lastatom;        /* Last relevant atom for a slab                  */
+    rvec  *slab_center;     /* Gaussian-weighted slab center                  */
+    rvec  *slab_center_ref; /* Gaussian-weighted slab center for the
+                               reference positions                            */
+    real  *slab_weights;    /* Sum of gaussian weights in a slab              */
+    real  *slab_torque_v;   /* Torque T = r x f for each slab.                */
+                            /* torque_v = m.v = angular momentum in the 
+                               direction of v                                 */
+    real  max_beta;         /* min_gaussian from inputrec->rotgrp is the
+                               minimum value the gaussian must have so that 
+                               the force is actually evaluated max_beta is 
+                               just another way to put it                     */
+    real  *gn_atom;         /* Precalculated gaussians for a single atom      */
+    int   *gn_slabind;      /* Tells to which slab each precalculated gaussian 
+                               belongs                                        */
+    rvec  *slab_innersumvec;/* Inner sum of the flexible2 potential per slab;
+                               this is precalculated for optimization reasons */
+    t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights 
+                               of atoms belonging to a slab                   */
+
+    /* For potential fits with varying angle: */
+    t_gmx_potfit *PotAngleFit;  /* Used for fit type 'potential'              */
+} t_gmx_enfrotgrp;
+
+
+/* Activate output of forces for correctness checks */
+/* #define PRINT_FORCES */
+#ifdef PRINT_FORCES
+#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]);
+#define PRINT_POT_TAU  if (MASTER(cr)) { \
+                           fprintf(stderr,"potential = %15.8f\n" "torque    = %15.8f\n", erg->V, erg->torque_v); \
+                       }
+#else
+#define PRINT_FORCE_J
+#define PRINT_POT_TAU
+#endif
+
+/* Shortcuts for often used queries */
+#define ISFLEX(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) )
+#define ISCOLL(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) || (rg->eType==erotgRMPF) || (rg->eType==erotgRM2PF) )
+
+
+/* Does any of the rotation groups use slab decomposition? */
+static gmx_bool HaveFlexibleGroups(t_rot *rot)
+{
+    int g;
+    t_rotgrp *rotg;
+
+
+    for (g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        if (ISFLEX(rotg))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+/* Is for any group the fit angle determined by finding the minimum of the
+ * rotation potential? */
+static gmx_bool HavePotFitGroups(t_rot *rot)
+{
+    int g;
+    t_rotgrp *rotg;
+
+
+    for (g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        if (erotgFitPOT == rotg->eFittype)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+static double** allocate_square_matrix(int dim)
+{
+    int i;
+    double** mat = NULL; 
+    
+    
+    snew(mat, dim);
+    for(i=0; i<dim; i++)
+        snew(mat[i], dim);
+
+    return mat;
+}
+
+
+static void free_square_matrix(double** mat, int dim)
+{
+    int i;
+    
+    
+    for (i=0; i<dim; i++)
+        sfree(mat[i]);
+    sfree(mat);
+}
+
+
+/* Return the angle for which the potential is minimal */
+static real get_fitangle(t_rotgrp *rotg, gmx_enfrotgrp_t erg)
+{
+    int i;
+    real fitangle = -999.9;
+    real pot_min = GMX_FLOAT_MAX;
+    t_gmx_potfit *fit;
+
+
+    fit = erg->PotAngleFit;
+
+    for (i = 0; i < rotg->PotAngle_nstep; i++)
+    {
+        if (fit->V[i] < pot_min)
+        {
+            pot_min = fit->V[i];
+            fitangle = fit->degangle[i];
+        }
+    }
+
+    return fitangle;
+}
+
+
+/* Reduce potential angle fit data for this group at this time step? */
+static gmx_inline gmx_bool bPotAngle(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
+{
+    return ( (erotgFitPOT==rotg->eFittype) && (do_per_step(step, rot->nstsout) || do_per_step(step, rot->nstrout)) );
+}
+
+/* Reduce slab torqe data for this group at this time step? */
+static gmx_inline gmx_bool bSlabTau(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
+{
+    return ( (ISFLEX(rotg)) && do_per_step(step, rot->nstsout) );
+}
+
+/* Output rotation energy, torques, etc. for each rotation group */
+static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
+{
+    int      g,i,islab,nslabs=0;
+    int      count;      /* MPI element counter                               */
+    t_rotgrp *rotg;
+    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
+    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
+    real     fitangle;
+    gmx_bool bFlex;
+
+    
+    er=rot->enfrot;
+    
+    /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
+     * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
+    if (PAR(cr))
+    {
+        count=0;
+        for (g=0; g < rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            erg = rotg->enfrotgrp;
+            nslabs = erg->slab_last - erg->slab_first + 1;
+            er->mpi_inbuf[count++] = erg->V;
+            er->mpi_inbuf[count++] = erg->torque_v;
+            er->mpi_inbuf[count++] = erg->angle_v;
+            er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
+
+            if (bPotAngle(rot, rotg, step))
+            {
+                for (i = 0; i < rotg->PotAngle_nstep; i++)
+                    er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
+            }
+            if (bSlabTau(rot, rotg, step))
+            {
+                for (i=0; i<nslabs; i++)
+                    er->mpi_inbuf[count++] = erg->slab_torque_v[i];
+            }
+        }
+        if (count > er->mpi_bufsize)
+            gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
+
+#ifdef GMX_MPI
+        MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
+#endif
+
+        /* Copy back the reduced data from the buffer on the master */
+        if (MASTER(cr))
+        {
+            count=0;
+            for (g=0; g < rot->ngrp; g++)
+            {
+                rotg = &rot->grp[g];
+                erg = rotg->enfrotgrp;
+                nslabs = erg->slab_last - erg->slab_first + 1;
+                erg->V        = er->mpi_outbuf[count++];
+                erg->torque_v = er->mpi_outbuf[count++];
+                erg->angle_v  = er->mpi_outbuf[count++];
+                erg->weight_v = er->mpi_outbuf[count++];
+
+                if (bPotAngle(rot, rotg, step))
+                {
+                    for (i = 0; i < rotg->PotAngle_nstep; i++)
+                        erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
+                }
+                if (bSlabTau(rot, rotg, step))
+                {
+                    for (i=0; i<nslabs; i++)
+                        erg->slab_torque_v[i] = er->mpi_outbuf[count++];
+                }
+            }
+        }
+    }
+    
+    /* Output */
+    if (MASTER(cr))
+    {
+        /* Angle and torque for each rotation group */
+        for (g=0; g < rot->ngrp; g++)
+        {
+            rotg=&rot->grp[g];
+            bFlex = ISFLEX(rotg);
+
+            erg=rotg->enfrotgrp;
+            
+            /* Output to main rotation output file: */
+            if ( do_per_step(step, rot->nstrout) )
+            {
+                if (erotgFitPOT == rotg->eFittype)
+                {
+                    fitangle = get_fitangle(rotg, erg);
+                }
+                else
+                {
+                    if (bFlex)
+                        fitangle = erg->angle_v; /* RMSD fit angle */
+                    else
+                        fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
+                }
+                fprintf(er->out_rot, "%12.4f", fitangle);
+                fprintf(er->out_rot, "%12.3e", erg->torque_v);
+                fprintf(er->out_rot, "%12.3e", erg->V);
+            }
+
+            if ( do_per_step(step, rot->nstsout) )
+            {
+                /* Output to torque log file: */
+                if (bFlex)
+                {
+                    fprintf(er->out_torque, "%12.3e%6d", t, g);
+                    for (i=erg->slab_first; i<=erg->slab_last; i++)
+                    {
+                        islab = i - erg->slab_first;  /* slab index */
+                        /* Only output if enough weight is in slab */
+                        if (erg->slab_weights[islab] > rotg->min_gaussian)
+                            fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
+                    }
+                    fprintf(er->out_torque , "\n");
+                }
+
+                /* Output to angles log file: */
+                if (erotgFitPOT == rotg->eFittype)
+                {
+                    fprintf(er->out_angles, "%12.3e%6d%12.4f", t, g, erg->degangle);
+                    /* Output energies at a set of angles around the reference angle */
+                    for (i = 0; i < rotg->PotAngle_nstep; i++)
+                        fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
+                    fprintf(er->out_angles, "\n");
+                }
+            }
+        }
+        if ( do_per_step(step, rot->nstrout) )
+            fprintf(er->out_rot, "\n");
+    }
+}
+
+
+/* Add the forces from enforced rotation potential to the local forces.
+ * Should be called after the SR forces have been evaluated */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
+{
+    int g,l,ii;
+    t_rotgrp *rotg;
+    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
+    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
+    real Vrot = 0.0;     /* If more than one rotation group is present, Vrot
+                            assembles the local parts from all groups         */
+
+    
+    er=rot->enfrot;
+    
+    /* Loop over enforced rotation groups (usually 1, though)
+     * Apply the forces from rotation potentials */
+    for (g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        erg=rotg->enfrotgrp;
+        Vrot += erg->V;  /* add the local parts from the nodes */
+        for (l=0; l<erg->nat_loc; l++)
+        {
+            /* Get the right index of the local force */
+            ii = erg->ind_loc[l];
+            /* Add */
+            rvec_inc(f[ii],erg->f_rot_loc[l]);
+        }
+    }
+
+    /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
+     * on the master and output these values to file. */
+    if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
+        reduce_output(cr, rot, t, step);
+
+    /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
+    er->bOut = TRUE;
+    
+    PRINT_POT_TAU
+
+    return Vrot;
+}
+
+
+/* The Gaussian norm is chosen such that the sum of the gaussian functions
+ * over the slabs is approximately 1.0 everywhere */
+#define GAUSS_NORM   0.569917543430618
+
+
+/* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
+ * also does some checks
+ */
+static double calc_beta_max(real min_gaussian, real slab_dist)
+{
+    double sigma;
+    double arg;
+    
+    
+    /* Actually the next two checks are already made in grompp */
+    if (slab_dist <= 0)
+        gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
+    if (min_gaussian <= 0)
+        gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
+
+    /* Define the sigma value */
+    sigma = 0.7*slab_dist;
+
+    /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
+    arg = min_gaussian/GAUSS_NORM;
+    if (arg > 1.0)
+        gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
+    
+    return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
+}
+
+
+static gmx_inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
+{
+    return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
+}
+
+
+static gmx_inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
+{
+    const real norm = GAUSS_NORM;
+    real       sigma;
+
+    
+    /* Define the sigma value */
+    sigma = 0.7*rotg->slab_dist;
+    /* Calculate the Gaussian value of slab n for position curr_x */
+    return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
+}
+
+
+/* Returns the weight in a single slab, also calculates the Gaussian- and mass-
+ * weighted sum of positions for that slab */
+static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
+{
+    rvec curr_x;              /* The position of an atom                      */
+    rvec curr_x_weighted;     /* The gaussian-weighted position               */
+    real gaussian;            /* A single gaussian weight                     */
+    real wgauss;              /* gaussian times current mass                  */
+    real slabweight = 0.0;    /* The sum of weights in the slab               */
+    int i,islab;
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data      */
+
+    
+    erg=rotg->enfrotgrp;
+    clear_rvec(*x_weighted_sum);
+    
+    /* Slab index */
+    islab = j - erg->slab_first;
+    
+    /* Loop over all atoms in the rotation group */
+     for (i=0; i<rotg->nat; i++)
+     {
+         copy_rvec(xc[i], curr_x);
+         gaussian = gaussian_weight(curr_x, rotg, j);
+         wgauss = gaussian * mc[i];
+         svmul(wgauss, curr_x, curr_x_weighted);
+         rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
+         slabweight += wgauss;
+     } /* END of loop over rotation group atoms */
+
+     return slabweight;
+}
+
+
+static void get_slab_centers(
+        t_rotgrp *rotg,       /* The rotation group information               */
+        rvec      *xc,        /* The rotation group positions; will 
+                                 typically be enfrotgrp->xc, but at first call 
+                                 it is enfrotgrp->xc_ref                      */
+        real      *mc,        /* The masses of the rotation group atoms       */
+        int       g,          /* The number of the rotation group             */
+        real      time,       /* Used for output only                         */
+        FILE      *out_slabs, /* For outputting center per slab information   */
+        gmx_bool  bOutStep,   /* Is this an output step?                      */
+        gmx_bool  bReference) /* If this routine is called from
+                                 init_rot_group we need to store
+                                 the reference slab centers                   */
+{
+    int j,islab;
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+    
+    
+    erg=rotg->enfrotgrp;
+
+    /* Loop over slabs */
+    for (j = erg->slab_first; j <= erg->slab_last; j++)
+    {
+        islab = j - erg->slab_first;
+        erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
+        
+        /* We can do the calculations ONLY if there is weight in the slab! */
+        if (erg->slab_weights[islab] > WEIGHT_MIN)
+        {
+            svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
+        }
+        else
+        {
+            /* We need to check this here, since we divide through slab_weights
+             * in the flexible low-level routines! */
+            gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
+        }
+        
+        /* At first time step: save the centers of the reference structure */
+        if (bReference)
+            copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
+    } /* END of loop over slabs */
+    
+    /* Output on the master */
+    if ( (NULL != out_slabs) && bOutStep)
+    {
+        fprintf(out_slabs, "%12.3e%6d", time, g);
+        for (j = erg->slab_first; j <= erg->slab_last; j++)
+        {
+            islab = j - erg->slab_first;
+            fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
+                    j,erg->slab_center[islab][XX],erg->slab_center[islab][YY],erg->slab_center[islab][ZZ]);
+        }
+        fprintf(out_slabs, "\n");
+    }
+}
+
+
+static void calc_rotmat(
+        rvec vec,
+        real degangle,  /* Angle alpha of rotation at time t in degrees       */
+        matrix rotmat)  /* Rotation matrix                                    */
+{
+    real radangle;            /* Rotation angle in radians */
+    real cosa;                /* cosine alpha              */
+    real sina;                /* sine alpha                */
+    real OMcosa;              /* 1 - cos(alpha)            */
+    real dumxy, dumxz, dumyz; /* save computations         */
+    rvec rot_vec;             /* Rotate around rot_vec ... */
+
+
+    radangle = degangle * M_PI/180.0;
+    copy_rvec(vec , rot_vec );
+
+    /* Precompute some variables: */
+    cosa   = cos(radangle);
+    sina   = sin(radangle);
+    OMcosa = 1.0 - cosa;
+    dumxy  = rot_vec[XX]*rot_vec[YY]*OMcosa;
+    dumxz  = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
+    dumyz  = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
+
+    /* Construct the rotation matrix for this rotation group: */
+    /* 1st column: */
+    rotmat[XX][XX] = cosa  + rot_vec[XX]*rot_vec[XX]*OMcosa;
+    rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
+    rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
+    /* 2nd column: */
+    rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
+    rotmat[YY][YY] = cosa  + rot_vec[YY]*rot_vec[YY]*OMcosa;
+    rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
+    /* 3rd column: */
+    rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
+    rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
+    rotmat[ZZ][ZZ] = cosa  + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
+
+#ifdef PRINTMATRIX
+    int iii,jjj;
+
+    for (iii=0; iii<3; iii++) {
+        for (jjj=0; jjj<3; jjj++)
+            fprintf(stderr, " %10.8f ",  rotmat[iii][jjj]);
+        fprintf(stderr, "\n");
+    }
+#endif
+}
+
+
+/* Calculates torque on the rotation axis tau = position x force */
+static gmx_inline real torque(
+        rvec rotvec,  /* rotation vector; MUST be normalized!                 */
+        rvec force,   /* force                                                */
+        rvec x,       /* position of atom on which the force acts             */
+        rvec pivot)   /* pivot point of rotation axis                         */
+{
+    rvec vectmp, tau;
+
+    
+    /* Subtract offset */
+    rvec_sub(x,pivot,vectmp);
+    
+    /* position x force */
+    cprod(vectmp, force, tau);
+    
+    /* Return the part of the torque which is parallel to the rotation vector */
+    return iprod(tau, rotvec);
+}
+
+
+/* Right-aligned output of value with standard width */
+static void print_aligned(FILE *fp, char *str)
+{
+    fprintf(fp, "%12s", str);
+}
+
+
+/* Right-aligned output of value with standard short width */
+static void print_aligned_short(FILE *fp, char *str)
+{
+    fprintf(fp, "%6s", str);
+}
+
+
+static FILE *open_output_file(const char *fn, int steps, const char what[])
+{
+    FILE *fp;
+    
+    
+    fp = ffopen(fn, "w");
+
+    fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
+            what,steps, steps>1 ? "s":"");
+    
+    return fp;
+}
+
+
+/* Open output file for slab center data. Call on master only */
+static FILE *open_slab_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+    FILE      *fp;
+    int       g,i;
+    t_rotgrp  *rotg;
+
+
+    if (rot->enfrot->Flags & MD_APPENDFILES)
+    {
+        fp = gmx_fio_fopen(fn,"a");
+    }
+    else
+    {
+        fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
+
+        for (g=0; g<rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            if (ISFLEX(rotg))
+            {
+                fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
+                        g, erotg_names[rotg->eType], rotg->slab_dist,
+                        rotg->bMassW? "centers of mass":"geometrical centers");
+            }
+        }
+
+        fprintf(fp, "# Reference centers are listed first (t=-1).\n");
+        fprintf(fp, "# The following columns have the syntax:\n");
+        fprintf(fp, "#     ");
+        print_aligned_short(fp, "t");
+        print_aligned_short(fp, "grp");
+        /* Print legend for the first two entries only ... */
+        for (i=0; i<2; i++)
+        {
+            print_aligned_short(fp, "slab");
+            print_aligned(fp, "X center");
+            print_aligned(fp, "Y center");
+            print_aligned(fp, "Z center");
+        }
+        fprintf(fp, " ...\n");
+        fflush(fp);
+    }
+
+    return fp;
+}
+
+
+/* Adds 'buf' to 'str' */
+static void add_to_string(char **str, char *buf)
+{
+    int len;
+
+
+    len = strlen(*str) + strlen(buf) + 1;
+    srenew(*str, len);
+    strcat(*str, buf);
+}
+
+
+static void add_to_string_aligned(char **str, char *buf)
+{
+    char buf_aligned[STRLEN];
+
+    sprintf(buf_aligned, "%12s", buf);
+    add_to_string(str, buf_aligned);
+}
+
+
+/* Open output file and print some general information about the rotation groups.
+ * Call on master only */
+static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+    FILE       *fp;
+    int        g,nsets;
+    t_rotgrp   *rotg;
+    const char **setname;
+    char       buf[50], buf2[75];
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    gmx_bool   bFlex;
+    char       *LegendStr=NULL;
+
+
+    if (rot->enfrot->Flags & MD_APPENDFILES)
+    {
+        fp = gmx_fio_fopen(fn,"a");
+    }
+    else
+    {
+        fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles (degrees) and energies (kJ/mol)", oenv);
+        fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s":"");
+        fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector v.\n");
+        fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
+        fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
+        fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
+        
+        for (g=0; g<rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            erg=rotg->enfrotgrp;
+            bFlex = ISFLEX(rotg);
+
+            fprintf(fp, "#\n");
+            fprintf(fp, "# ROTATION GROUP %d, potential type '%s':\n"      , g, erotg_names[rotg->eType]);
+            fprintf(fp, "# rot_massw%d          %s\n"                      , g, yesno_names[rotg->bMassW]);
+            fprintf(fp, "# rot_vec%d            %12.5e %12.5e %12.5e\n"    , g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+            fprintf(fp, "# rot_rate%d           %12.5e degrees/ps\n"       , g, rotg->rate);
+            fprintf(fp, "# rot_k%d              %12.5e kJ/(mol*nm^2)\n"    , g, rotg->k);
+            if ( rotg->eType==erotgISO || rotg->eType==erotgPM || rotg->eType==erotgRM || rotg->eType==erotgRM2)
+                fprintf(fp, "# rot_pivot%d          %12.5e %12.5e %12.5e  nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
+
+            if (bFlex)
+            {
+                fprintf(fp, "# rot_slab_distance%d   %f nm\n", g, rotg->slab_dist);
+                fprintf(fp, "# rot_min_gaussian%d   %12.5e\n", g, rotg->min_gaussian);
+            }
+
+            /* Output the centers of the rotation groups for the pivot-free potentials */
+            if ((rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF
+                || (rotg->eType==erotgFLEXT) || (rotg->eType==erotgFLEX2T)) )
+            {
+                fprintf(fp, "# ref. grp. %d center  %12.5e %12.5e %12.5e\n", g,
+                            erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
+
+                fprintf(fp, "# grp. %d init.center  %12.5e %12.5e %12.5e\n", g,
+                            erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
+            }
+
+            if ( (rotg->eType == erotgRM2) || (rotg->eType==erotgFLEX2) || (rotg->eType==erotgFLEX2T) )
+            {
+                fprintf(fp, "# rot_eps%d            %12.5e nm^2\n", g, rotg->eps);
+            }
+            if (erotgFitPOT == rotg->eFittype)
+            {
+                fprintf(fp, "#\n");
+                fprintf(fp, "# theta_fit%d is determined by first evaluating the potential for %d angles around theta_ref%d.\n",
+                            g, rotg->PotAngle_nstep, g);
+                fprintf(fp, "# The fit angle is the one with the smallest potential. It is given as the deviation\n");
+                fprintf(fp, "# from the reference angle, i.e. if theta_ref=X and theta_fit=Y, then the angle with\n");
+                fprintf(fp, "# minimal value of the potential is X+Y. Angular resolution is %g degrees.\n", rotg->PotAngle_step);
+            }
+        }
+        
+        /* Print a nice legend */
+        snew(LegendStr, 1);
+        LegendStr[0] = '\0';
+        sprintf(buf, "#     %6s", "time");
+        add_to_string_aligned(&LegendStr, buf);
+
+        nsets = 0;
+        snew(setname, 4*rot->ngrp);
+        
+        for (g=0; g<rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            sprintf(buf, "theta_ref%d", g);
+            add_to_string_aligned(&LegendStr, buf);
+
+            sprintf(buf2, "%s (degrees)", buf);
+            setname[nsets] = strdup(buf2);
+            nsets++;
+        }
+        for (g=0; g<rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            bFlex = ISFLEX(rotg);
+
+            /* For flexible axis rotation we use RMSD fitting to determine the
+             * actual angle of the rotation group */
+            if (bFlex || erotgFitPOT == rotg->eFittype)
+                sprintf(buf, "theta_fit%d", g);
+            else
+                sprintf(buf, "theta_av%d", g);
+            add_to_string_aligned(&LegendStr, buf);
+            sprintf(buf2, "%s (degrees)", buf);
+            setname[nsets] = strdup(buf2);
+            nsets++;
+
+            sprintf(buf, "tau%d", g);
+            add_to_string_aligned(&LegendStr, buf);
+            sprintf(buf2, "%s (kJ/mol)", buf);
+            setname[nsets] = strdup(buf2);
+            nsets++;
+
+            sprintf(buf, "energy%d", g);
+            add_to_string_aligned(&LegendStr, buf);
+            sprintf(buf2, "%s (kJ/mol)", buf);
+            setname[nsets] = strdup(buf2);
+            nsets++;
+        }
+        fprintf(fp, "#\n");
+        
+        if (nsets > 1)
+            xvgr_legend(fp, nsets, setname, oenv);
+        sfree(setname);
+
+        fprintf(fp, "#\n# Legend for the following data columns:\n");
+        fprintf(fp, "%s\n", LegendStr);
+        sfree(LegendStr);
+        
+        fflush(fp);
+    }
+    
+    return fp;
+}
+
+
+/* Call on master only */
+static FILE *open_angles_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+    int      g,i;
+    FILE     *fp;
+    t_rotgrp *rotg;
+    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
+    char     buf[100];
+
+
+    if (rot->enfrot->Flags & MD_APPENDFILES)
+    {
+        fp = gmx_fio_fopen(fn,"a");
+    }
+    else
+    {
+        /* Open output file and write some information about it's structure: */
+        fp = open_output_file(fn, rot->nstsout, "rotation group angles");
+        fprintf(fp, "# All angles given in degrees, time in ps.\n");
+        for (g=0; g<rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            erg=rotg->enfrotgrp;
+
+            /* Output for this group happens only if potential type is flexible or
+             * if fit type is potential! */
+            if ( ISFLEX(rotg) || (erotgFitPOT == rotg->eFittype) )
+            {
+                if (ISFLEX(rotg))
+                    sprintf(buf, " slab distance %f nm, ", rotg->slab_dist);
+                else
+                    buf[0] = '\0';
+
+                fprintf(fp, "#\n# ROTATION GROUP %d '%s',%s fit type '%s'.\n",
+                        g, erotg_names[rotg->eType], buf, erotg_fitnames[rotg->eFittype]);
+
+                /* Special type of fitting using the potential minimum. This is
+                 * done for the whole group only, not for the individual slabs. */
+                if (erotgFitPOT == rotg->eFittype)
+                {
+                    fprintf(fp, "#    To obtain theta_fit%d, the potential is evaluated for %d angles around theta_ref%d\n", g, rotg->PotAngle_nstep, g);
+                    fprintf(fp, "#    The fit angle in the rotation standard outfile is the one with minimal energy E(theta_fit) [kJ/mol].\n");
+                    fprintf(fp, "#\n");
+                }
+
+                fprintf(fp, "# Legend for the group %d data columns:\n", g);
+                fprintf(fp, "#     ");
+                print_aligned_short(fp, "time");
+                print_aligned_short(fp, "grp");
+                print_aligned(fp, "theta_ref");
+
+                if (erotgFitPOT == rotg->eFittype)
+                {
+                    /* Output the set of angles around the reference angle */
+                    for (i = 0; i < rotg->PotAngle_nstep; i++)
+                    {
+                        sprintf(buf, "E(%g)", erg->PotAngleFit->degangle[i]);
+                        print_aligned(fp, buf);
+                    }
+                }
+                else
+                {
+                    /* Output fit angle for each slab */
+                    print_aligned_short(fp, "slab");
+                    print_aligned_short(fp, "atoms");
+                    print_aligned(fp, "theta_fit");
+                    print_aligned_short(fp, "slab");
+                    print_aligned_short(fp, "atoms");
+                    print_aligned(fp, "theta_fit");
+                    fprintf(fp, " ...");
+                }
+                fprintf(fp, "\n");
+            }
+        }
+        fflush(fp);
+    }
+
+    return fp;
+}
+
+
+/* Open torque output file and write some information about it's structure.
+ * Call on master only */
+static FILE *open_torque_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+    FILE      *fp;
+    int       g;
+    t_rotgrp  *rotg;
+
+
+    if (rot->enfrot->Flags & MD_APPENDFILES)
+    {
+        fp = gmx_fio_fopen(fn,"a");
+    }
+    else
+    {
+        fp = open_output_file(fn, rot->nstsout,"torques");
+
+        for (g=0; g<rot->ngrp; g++)
+        {
+            rotg = &rot->grp[g];
+            if (ISFLEX(rotg))
+            {
+                fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
+                fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector.\n");
+                fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
+                fprintf(fp, "# rot_vec%d            %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+                fprintf(fp, "#\n");
+            }
+        }
+        fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
+        fprintf(fp, "#     ");
+        print_aligned_short(fp, "t");
+        print_aligned_short(fp, "grp");
+        print_aligned_short(fp, "slab");
+        print_aligned(fp, "tau");
+        print_aligned_short(fp, "slab");
+        print_aligned(fp, "tau");
+        fprintf(fp, " ...\n");
+        fflush(fp);
+    }
+
+    return fp;
+}
+
+
+static void swap_val(double* vec, int i, int j)
+{
+    double tmp = vec[j];
+    
+    
+    vec[j]=vec[i];
+    vec[i]=tmp;
+}
+
+
+static void swap_col(double **mat, int i, int j)
+{
+    double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
+    
+    
+    mat[0][j]=mat[0][i];
+    mat[1][j]=mat[1][i];
+    mat[2][j]=mat[2][i];
+    
+    mat[0][i]=tmp[0];
+    mat[1][i]=tmp[1];
+    mat[2][i]=tmp[2];
+} 
+
+
+/* Eigenvectors are stored in columns of eigen_vec */
+static void diagonalize_symmetric(
+        double **matrix,
+        double **eigen_vec,
+        double eigenval[3])
+{
+    int n_rot;
+    
+    
+    jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
+    
+    /* sort in ascending order */
+    if (eigenval[0] > eigenval[1])
+    {
+        swap_val(eigenval, 0, 1);
+        swap_col(eigen_vec, 0, 1);
+    } 
+    if (eigenval[1] > eigenval[2])
+    {
+        swap_val(eigenval, 1, 2);
+        swap_col(eigen_vec, 1, 2);
+    }
+    if (eigenval[0] > eigenval[1])
+    {
+        swap_val(eigenval, 0, 1);
+        swap_col(eigen_vec, 0, 1);
+    }
+}
+
+
+static void align_with_z(
+        rvec* s,           /* Structure to align */
+        int natoms,
+        rvec axis)
+{
+    int    i, j, k;
+    rvec   zet = {0.0, 0.0, 1.0};
+    rvec   rot_axis={0.0, 0.0, 0.0};
+    rvec   *rotated_str=NULL;
+    real   ooanorm;
+    real   angle;
+    matrix rotmat;
+    
+    
+    snew(rotated_str, natoms);
+
+    /* Normalize the axis */
+    ooanorm = 1.0/norm(axis);
+    svmul(ooanorm, axis, axis);
+    
+    /* Calculate the angle for the fitting procedure */
+    cprod(axis, zet, rot_axis);
+    angle = acos(axis[2]);
+    if (angle < 0.0)
+        angle += M_PI;
+    
+    /* Calculate the rotation matrix */
+    calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
+    
+    /* Apply the rotation matrix to s */
+    for (i=0; i<natoms; i++)
+    {    
+        for(j=0; j<3; j++)
+        {
+            for(k=0; k<3; k++)
+            {
+                rotated_str[i][j] += rotmat[j][k]*s[i][k];
+            }
+        }
+    }
+    
+    /* Rewrite the rotated structure to s */
+    for(i=0; i<natoms; i++)
+    {
+        for(j=0; j<3; j++)
+        {
+            s[i][j]=rotated_str[i][j];
+        }
+    }
+    
+    sfree(rotated_str);
+} 
+
+
+static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
+{    
+    int i, j, k;
+    
+    for (i=0; i<3; i++)
+        for (j=0; j<3; j++)
+            Rmat[i][j] = 0.0;
+    
+    for (i=0; i<3; i++) 
+        for (j=0; j<3; j++) 
+            for (k=0; k<natoms; k++) 
+                Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
+}
+
+
+static void weigh_coords(rvec* str, real* weight, int natoms)
+{
+    int i, j;
+    
+    
+    for(i=0; i<natoms; i++)
+    {
+        for(j=0; j<3; j++)
+            str[i][j] *= sqrt(weight[i]);
+    }  
+}
+
+
+static real opt_angle_analytic(
+        rvec* ref_s,
+        rvec* act_s,
+        real* weight, 
+        int natoms,
+        rvec ref_com,
+        rvec act_com,
+        rvec axis)
+{    
+    int    i, j, k;
+    rvec   *ref_s_1=NULL;
+    rvec   *act_s_1=NULL;
+    rvec   shift;
+    double **Rmat, **RtR, **eigvec;
+    double eigval[3];
+    double V[3][3], WS[3][3];
+    double rot_matrix[3][3];
+    double opt_angle;
+    
+    
+    /* Do not change the original coordinates */ 
+    snew(ref_s_1, natoms);
+    snew(act_s_1, natoms);
+    for(i=0; i<natoms; i++)
+    {
+        copy_rvec(ref_s[i], ref_s_1[i]);
+        copy_rvec(act_s[i], act_s_1[i]);
+    }
+    
+    /* Translate the structures to the origin */
+    shift[XX] = -ref_com[XX];
+    shift[YY] = -ref_com[YY];
+    shift[ZZ] = -ref_com[ZZ];
+    translate_x(ref_s_1, natoms, shift);
+    
+    shift[XX] = -act_com[XX];
+    shift[YY] = -act_com[YY];
+    shift[ZZ] = -act_com[ZZ];
+    translate_x(act_s_1, natoms, shift);
+    
+    /* Align rotation axis with z */
+    align_with_z(ref_s_1, natoms, axis);
+    align_with_z(act_s_1, natoms, axis);
+    
+    /* Correlation matrix */
+    Rmat = allocate_square_matrix(3);
+    
+    for (i=0; i<natoms; i++)
+    {
+        ref_s_1[i][2]=0.0;
+        act_s_1[i][2]=0.0;
+    }
+    
+    /* Weight positions with sqrt(weight) */
+    if (NULL != weight)
+    {
+        weigh_coords(ref_s_1, weight, natoms);
+        weigh_coords(act_s_1, weight, natoms);
+    }
+    
+    /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
+    calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
+    
+    /* Calculate RtR */
+    RtR = allocate_square_matrix(3);
+    for (i=0; i<3; i++)
+    {
+        for (j=0; j<3; j++)
+        {
+            for (k=0; k<3; k++)
+            {
+                RtR[i][j] += Rmat[k][i] * Rmat[k][j];
+            }
+        }
+    }
+    /* Diagonalize RtR */
+    snew(eigvec,3);
+    for (i=0; i<3; i++)
+        snew(eigvec[i],3);
+    
+    diagonalize_symmetric(RtR, eigvec, eigval);
+    swap_col(eigvec,0,1);
+    swap_col(eigvec,1,2);
+    swap_val(eigval,0,1);
+    swap_val(eigval,1,2);
+    
+    /* Calculate V */
+    for(i=0; i<3; i++)
+    {
+        for(j=0; j<3; j++)
+        {
+            V[i][j]  = 0.0;
+            WS[i][j] = 0.0;
+        }
+    }
+    
+    for (i=0; i<2; i++)
+        for (j=0; j<2; j++)
+            WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
+    
+    for (i=0; i<3; i++)
+    {
+        for (j=0; j<3; j++)
+        {
+            for (k=0; k<3; k++)
+            {
+                V[i][j] += Rmat[i][k]*WS[k][j];
+            }
+        }
+    }
+    free_square_matrix(Rmat, 3);
+    
+    /* Calculate optimal rotation matrix */
+    for (i=0; i<3; i++)
+        for (j=0; j<3; j++)
+            rot_matrix[i][j] = 0.0;
+    
+    for (i=0; i<3; i++)
+    {
+        for(j=0; j<3; j++)
+        {
+            for(k=0; k<3; k++){
+                rot_matrix[i][j] += eigvec[i][k]*V[j][k];
+            }
+        }
+    }
+    rot_matrix[2][2] = 1.0;
+        
+    /* In some cases abs(rot_matrix[0][0]) can be slighly larger
+     * than unity due to numerical inacurracies. To be able to calculate
+     * the acos function, we put these values back in range. */
+    if (rot_matrix[0][0] > 1.0)
+    {
+        rot_matrix[0][0] = 1.0;
+    }
+    else if (rot_matrix[0][0] < -1.0)
+    {
+        rot_matrix[0][0] = -1.0;
+    }
+
+    /* Determine the optimal rotation angle: */
+    opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
+    if (rot_matrix[0][1] < 0.0)
+        opt_angle = (-1.0)*opt_angle;
+        
+    /* Give back some memory */
+    free_square_matrix(RtR, 3);
+    sfree(ref_s_1);
+    sfree(act_s_1);
+    for (i=0; i<3; i++)
+        sfree(eigvec[i]);
+    sfree(eigvec);
+    
+    return (real) opt_angle;
+}
+
+
+/* Determine angle of the group by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static real flex_fit_angle(t_rotgrp *rotg)
+{
+    int         i;
+    rvec        *fitcoords=NULL;
+    rvec        center;         /* Center of positions passed to the fit routine */
+    real        fitangle;       /* Angle of the rotation group derived by fitting */
+    rvec        coord;
+    real        scal;
+    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
+
+    
+    erg=rotg->enfrotgrp;
+
+    /* Get the center of the rotation group.
+     * Note, again, erg->xc has been sorted in do_flexible */
+    get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
+
+    /* === Determine the optimal fit angle for the rotation group === */
+    if (rotg->eFittype == erotgFitNORM)
+    {
+        /* Normalize every position to it's reference length */
+        for (i=0; i<rotg->nat; i++)
+        {
+            /* Put the center of the positions into the origin */
+            rvec_sub(erg->xc[i], center, coord);
+            /* Determine the scaling factor for the length: */
+            scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
+            /* Get position, multiply with the scaling factor and save  */
+            svmul(scal, coord, erg->xc_norm[i]);
+        }
+        fitcoords = erg->xc_norm;
+    }
+    else
+    {
+        fitcoords = erg->xc;
+    }
+    /* From the point of view of the current positions, the reference has rotated
+     * backwards. Since we output the angle relative to the fixed reference,
+     * we need the minus sign. */
+    fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
+                                   rotg->nat, erg->xc_ref_center, center, rotg->vec);
+
+    return fitangle;
+}
+
+
+/* Determine actual angle of each slab by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static void flex_fit_angle_perslab(
+        int  g,
+        t_rotgrp *rotg,
+        double t,
+        real degangle,
+        FILE *fp)
+{
+    int         i,l,n,islab,ind;
+    rvec        curr_x, ref_x;
+    rvec        act_center;  /* Center of actual positions that are passed to the fit routine */
+    rvec        ref_center;  /* Same for the reference positions */
+    real        fitangle;    /* Angle of a slab derived from an RMSD fit to
+                              * the reference structure at t=0  */
+    t_gmx_slabdata *sd;
+    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
+    real        OOm_av;      /* 1/average_mass of a rotation group atom */
+    real        m_rel;       /* Relative mass of a rotation group atom  */
+
+
+    erg=rotg->enfrotgrp;
+
+    /* Average mass of a rotation group atom: */
+    OOm_av = erg->invmass*rotg->nat;
+
+    /**********************************/
+    /* First collect the data we need */
+    /**********************************/
+
+    /* Collect the data for the individual slabs */
+    for (n = erg->slab_first; n <= erg->slab_last; n++)
+    {
+        islab = n - erg->slab_first; /* slab index */
+        sd = &(rotg->enfrotgrp->slab_data[islab]);
+        sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
+        ind = 0;
+
+        /* Loop over the relevant atoms in the slab */
+        for (l=erg->firstatom[islab]; l<=erg->lastatom[islab]; l++)
+        {
+            /* Current position of this atom: x[ii][XX/YY/ZZ] */
+            copy_rvec(erg->xc[l], curr_x);
+
+            /* The (unrotated) reference position of this atom is copied to ref_x.
+             * Beware, the xc coords have been sorted in do_flexible */
+            copy_rvec(erg->xc_ref_sorted[l], ref_x);
+
+            /* Save data for doing angular RMSD fit later */
+            /* Save the current atom position */
+            copy_rvec(curr_x, sd->x[ind]);
+            /* Save the corresponding reference position */
+            copy_rvec(ref_x , sd->ref[ind]);
+
+            /* Maybe also mass-weighting was requested. If yes, additionally
+             * multiply the weights with the relative mass of the atom. If not,
+             * multiply with unity. */
+            m_rel = erg->mc_sorted[l]*OOm_av;
+
+            /* Save the weight for this atom in this slab */
+            sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
+
+            /* Next atom in this slab */
+            ind++;
+        }
+    }
+
+    /******************************/
+    /* Now do the fit calculation */
+    /******************************/
+
+    fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
+
+    /* === Now do RMSD fitting for each slab === */
+    /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
+#define SLAB_MIN_ATOMS 4
+
+    for (n = erg->slab_first; n <= erg->slab_last; n++)
+    {
+        islab = n - erg->slab_first; /* slab index */
+        sd = &(rotg->enfrotgrp->slab_data[islab]);
+        if (sd->nat >= SLAB_MIN_ATOMS)
+        {
+            /* Get the center of the slabs reference and current positions */
+            get_center(sd->ref, sd->weight, sd->nat, ref_center);
+            get_center(sd->x  , sd->weight, sd->nat, act_center);
+            if (rotg->eFittype == erotgFitNORM)
+            {
+                /* Normalize every position to it's reference length
+                 * prior to performing the fit */
+                for (i=0; i<sd->nat;i++) /* Center */
+                {
+                    rvec_dec(sd->ref[i], ref_center);
+                    rvec_dec(sd->x[i]  , act_center);
+                    /* Normalize x_i such that it gets the same length as ref_i */
+                    svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
+                }
+                /* We already subtracted the centers */
+                clear_rvec(ref_center);
+                clear_rvec(act_center);
+            }
+            fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
+                                           ref_center, act_center, rotg->vec);
+            fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
+        }
+    }
+    fprintf(fp     , "\n");
+
+#undef SLAB_MIN_ATOMS
+}
+
+
+/* Shift x with is */
+static gmx_inline void shift_single_coord(matrix box, rvec x, const ivec is)
+{
+    int tx,ty,tz;
+
+
+    tx=is[XX];
+    ty=is[YY];
+    tz=is[ZZ];
+
+    if(TRICLINIC(box))
+    {
+        x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
+        x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
+        x[ZZ] += tz*box[ZZ][ZZ];
+    } else
+    {
+        x[XX] += tx*box[XX][XX];
+        x[YY] += ty*box[YY][YY];
+        x[ZZ] += tz*box[ZZ][ZZ];
+    }
+}
+
+
+/* Determine the 'home' slab of this atom which is the
+ * slab with the highest Gaussian weight of all */
+#define round(a) (int)(a+0.5)
+static gmx_inline int get_homeslab(
+        rvec curr_x,   /* The position for which the home slab shall be determined */ 
+        rvec rotvec,   /* The rotation vector */
+        real slabdist) /* The slab distance */
+{
+    real dist;
+    
+    
+    /* The distance of the atom to the coordinate center (where the
+     * slab with index 0) is */
+    dist = iprod(rotvec, curr_x);
+    
+    return round(dist / slabdist); 
+}
+
+
+/* For a local atom determine the relevant slabs, i.e. slabs in
+ * which the gaussian is larger than min_gaussian
+ */
+static int get_single_atom_gaussians(
+        rvec      curr_x,
+        t_rotgrp  *rotg)
+{
+   int slab, homeslab;
+   real g;
+   int count = 0;
+   gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+
+   
+   erg=rotg->enfrotgrp;
+   
+   /* Determine the 'home' slab of this atom: */
+   homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
+
+   /* First determine the weight in the atoms home slab: */
+   g = gaussian_weight(curr_x, rotg, homeslab);
+   
+   erg->gn_atom[count] = g;
+   erg->gn_slabind[count] = homeslab;
+   count++;
+   
+   
+   /* Determine the max slab */
+   slab = homeslab;
+   while (g > rotg->min_gaussian)
+   {
+       slab++;
+       g = gaussian_weight(curr_x, rotg, slab);
+       erg->gn_slabind[count]=slab;
+       erg->gn_atom[count]=g;
+       count++;
+   }
+   count--;
+   
+   /* Determine the max slab */
+   slab = homeslab;
+   do
+   {
+       slab--;
+       g = gaussian_weight(curr_x, rotg, slab);       
+       erg->gn_slabind[count]=slab;
+       erg->gn_atom[count]=g;
+       count++;
+   }
+   while (g > rotg->min_gaussian);
+   count--;
+   
+   return count;
+}
+
+
+static void flex2_precalc_inner_sum(t_rotgrp *rotg)
+{
+    int  i,n,islab;
+    rvec  xi;                /* positions in the i-sum                        */
+    rvec  xcn, ycn;          /* the current and the reference slab centers    */
+    real gaussian_xi;
+    rvec yi0;
+    rvec  rin;               /* Helper variables                              */
+    real  fac,fac2;
+    rvec innersumvec;
+    real OOpsii,OOpsiistar;
+    real sin_rin;          /* s_ii.r_ii */
+    rvec s_in,tmpvec,tmpvec2;
+    real mi,wi;            /* Mass-weighting of the positions                 */
+    real N_M;              /* N/M                                             */
+    gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
+
+
+    erg=rotg->enfrotgrp;
+    N_M = rotg->nat * erg->invmass;
+
+    /* Loop over all slabs that contain something */
+    for (n=erg->slab_first; n <= erg->slab_last; n++)
+    {
+        islab = n - erg->slab_first; /* slab index */
+
+        /* The current center of this slab is saved in xcn: */
+        copy_rvec(erg->slab_center[islab], xcn);
+        /* ... and the reference center in ycn: */
+        copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+        /*** D. Calculate the whole inner sum used for second and third sum */
+        /* For slab n, we need to loop over all atoms i again. Since we sorted
+         * the atoms with respect to the rotation vector, we know that it is sufficient
+         * to calculate from firstatom to lastatom only. All other contributions will
+         * be very small. */
+        clear_rvec(innersumvec);
+        for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
+        {
+            /* Coordinate xi of this atom */
+            copy_rvec(erg->xc[i],xi);
+
+            /* The i-weights */
+            gaussian_xi = gaussian_weight(xi,rotg,n);
+            mi = erg->mc_sorted[i];  /* need the sorted mass here */
+            wi = N_M*mi;
+
+            /* Calculate rin */
+            copy_rvec(erg->xc_ref_sorted[i],yi0); /* Reference position yi0   */
+            rvec_sub(yi0, ycn, tmpvec2);          /* tmpvec2 = yi0 - ycn      */
+            mvmul(erg->rotmat, tmpvec2, rin);     /* rin = Omega.(yi0 - ycn)  */
+
+            /* Calculate psi_i* and sin */
+            rvec_sub(xi, xcn, tmpvec2);           /* tmpvec2 = xi - xcn       */
+            cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xi - xcn)  */
+            OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
+            OOpsii = norm(tmpvec);                /* OOpsii = 1 / psii = |v x (xi - xcn)| */
+
+                                       /*         v x (xi - xcn)          */
+            unitv(tmpvec, s_in);       /*  sin = ----------------         */
+                                       /*        |v x (xi - xcn)|         */
+
+            sin_rin=iprod(s_in,rin);   /* sin_rin = sin . rin             */
+
+            /* Now the whole sum */
+            fac = OOpsii/OOpsiistar;
+            svmul(fac, rin, tmpvec);
+            fac2 = fac*fac*OOpsii;
+            svmul(fac2*sin_rin, s_in, tmpvec2);
+            rvec_dec(tmpvec, tmpvec2);
+
+            svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
+
+            rvec_inc(innersumvec,tmpvec2);
+        } /* now we have the inner sum, used both for sum2 and sum3 */
+
+        /* Save it to be used in do_flex2_lowlevel */
+        copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+    } /* END of loop over slabs */
+}
+
+
+static void flex_precalc_inner_sum(t_rotgrp *rotg)
+{
+    int   i,n,islab;
+    rvec  xi;                /* position                                      */
+    rvec  xcn, ycn;          /* the current and the reference slab centers    */
+    rvec  qin,rin;           /* q_i^n and r_i^n                               */
+    real  bin;
+    rvec  tmpvec;
+    rvec  innersumvec;       /* Inner part of sum_n2                          */
+    real  gaussian_xi;       /* Gaussian weight gn(xi)                        */
+    real  mi,wi;             /* Mass-weighting of the positions               */
+    real  N_M;               /* N/M                                           */
+
+    gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
+
+
+    erg=rotg->enfrotgrp;
+    N_M = rotg->nat * erg->invmass;
+
+    /* Loop over all slabs that contain something */
+    for (n=erg->slab_first; n <= erg->slab_last; n++)
+    {
+        islab = n - erg->slab_first; /* slab index */
+
+        /* The current center of this slab is saved in xcn: */
+        copy_rvec(erg->slab_center[islab], xcn);
+        /* ... and the reference center in ycn: */
+        copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+        /* For slab n, we need to loop over all atoms i again. Since we sorted
+         * the atoms with respect to the rotation vector, we know that it is sufficient
+         * to calculate from firstatom to lastatom only. All other contributions will
+         * be very small. */
+        clear_rvec(innersumvec);
+        for (i=erg->firstatom[islab]; i<=erg->lastatom[islab]; i++)
+        {
+            /* Coordinate xi of this atom */
+            copy_rvec(erg->xc[i],xi);
+
+            /* The i-weights */
+            gaussian_xi = gaussian_weight(xi,rotg,n);
+            mi = erg->mc_sorted[i];  /* need the sorted mass here */
+            wi = N_M*mi;
+
+            /* Calculate rin and qin */
+            rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
+            mvmul(erg->rotmat, tmpvec, rin);      /* rin = Omega.(yi0 - ycn)  */
+            cprod(rotg->vec, rin, tmpvec);    /* tmpvec = v x Omega*(yi0-ycn) */
+
+                                             /*        v x Omega*(yi0-ycn)    */
+            unitv(tmpvec, qin);              /* qin = ---------------------   */
+                                             /*       |v x Omega*(yi0-ycn)|   */
+
+            /* Calculate bin */
+            rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
+            bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
+
+            svmul(wi*gaussian_xi*bin, qin, tmpvec);
+
+            /* Add this contribution to the inner sum: */
+            rvec_add(innersumvec, tmpvec, innersumvec);
+        } /* now we have the inner sum vector S^n for this slab */
+        /* Save it to be used in do_flex_lowlevel */
+        copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+    }
+}
+
+
+static real do_flex2_lowlevel(
+        t_rotgrp  *rotg,
+        real      sigma,    /* The Gaussian width sigma */
+        rvec      x[],
+        gmx_bool  bOutstepRot,
+        gmx_bool  bOutstepSlab,
+        matrix    box)
+{
+    int  count,ic,ii,j,m,n,islab,iigrp,ifit;
+    rvec xj;                 /* position in the i-sum                         */
+    rvec yj0;                /* the reference position in the j-sum           */
+    rvec xcn, ycn;           /* the current and the reference slab centers    */
+    real V;                  /* This node's part of the rotation pot. energy  */
+    real gaussian_xj;        /* Gaussian weight                               */
+    real beta;
+
+    real  numerator,fit_numerator;
+    rvec  rjn,fit_rjn;       /* Helper variables                              */
+    real  fac,fac2;
+
+    real OOpsij,OOpsijstar;
+    real OOsigma2;           /* 1/(sigma^2)                                   */
+    real sjn_rjn;
+    real betasigpsi;
+    rvec sjn,tmpvec,tmpvec2,yj0_ycn;
+    rvec sum1vec_part,sum1vec,sum2vec_part,sum2vec,sum3vec,sum4vec,innersumvec;
+    real sum3,sum4;
+    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
+    real mj,wj;              /* Mass-weighting of the positions               */
+    real N_M;                /* N/M                                           */
+    real Wjn;                /* g_n(x_j) m_j / Mjn                            */
+    gmx_bool bCalcPotFit;
+
+    /* To calculate the torque per slab */
+    rvec slab_force;         /* Single force from slab n on one atom          */
+    rvec slab_sum1vec_part;
+    real slab_sum3part,slab_sum4part;
+    rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
+
+
+    erg=rotg->enfrotgrp;
+
+    /* Pre-calculate the inner sums, so that we do not have to calculate
+     * them again for every atom */
+    flex2_precalc_inner_sum(rotg);
+
+    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+    /********************************************************/
+    /* Main loop over all local atoms of the rotation group */
+    /********************************************************/
+    N_M = rotg->nat * erg->invmass;
+    V = 0.0;
+    OOsigma2 = 1.0 / (sigma*sigma);
+    for (j=0; j<erg->nat_loc; j++)
+    {
+        /* Local index of a rotation group atom  */
+        ii = erg->ind_loc[j];
+        /* Position of this atom in the collective array */
+        iigrp = erg->xc_ref_ind[j];
+        /* Mass-weighting */
+        mj = erg->mc[iigrp];  /* need the unsorted mass here */
+        wj = N_M*mj;
+        
+        /* Current position of this atom: x[ii][XX/YY/ZZ]
+         * Note that erg->xc_center contains the center of mass in case the flex2-t
+         * potential was chosen. For the flex2 potential erg->xc_center must be
+         * zero. */
+        rvec_sub(x[ii], erg->xc_center, xj);
+
+        /* Shift this atom such that it is near its reference */
+        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+        /* Determine the slabs to loop over, i.e. the ones with contributions
+         * larger than min_gaussian */
+        count = get_single_atom_gaussians(xj, rotg);
+        
+        clear_rvec(sum1vec_part);
+        clear_rvec(sum2vec_part);
+        sum3 = 0.0;
+        sum4 = 0.0;
+        /* Loop over the relevant slabs for this atom */
+        for (ic=0; ic < count; ic++)  
+        {
+            n = erg->gn_slabind[ic];
+            
+            /* Get the precomputed Gaussian value of curr_slab for curr_x */
+            gaussian_xj = erg->gn_atom[ic];
+
+            islab = n - erg->slab_first; /* slab index */
+            
+            /* The (unrotated) reference position of this atom is copied to yj0: */
+            copy_rvec(rotg->x_ref[iigrp], yj0);
+
+            beta = calc_beta(xj, rotg,n);
+
+            /* The current center of this slab is saved in xcn: */
+            copy_rvec(erg->slab_center[islab], xcn);
+            /* ... and the reference center in ycn: */
+            copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+            
+            rvec_sub(yj0, ycn, yj0_ycn);          /* yj0_ycn = yj0 - ycn      */
+
+            /* Rotate: */
+            mvmul(erg->rotmat, yj0_ycn, rjn);     /* rjn = Omega.(yj0 - ycn)  */
+            
+            /* Subtract the slab center from xj */
+            rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
+
+            /* Calculate sjn */
+            cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xj - xcn)  */
+
+            OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
+
+            numerator = sqr(iprod(tmpvec, rjn));
+            
+            /*********************************/
+            /* Add to the rotation potential */
+            /*********************************/
+            V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
+
+            /* If requested, also calculate the potential for a set of angles
+             * near the current reference angle */
+            if (bCalcPotFit)
+            {
+                for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+                {
+                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
+                    fit_numerator = sqr(iprod(tmpvec, fit_rjn));
+                    erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
+                }
+            }
+
+            /*************************************/
+            /* Now calculate the force on atom j */
+            /*************************************/
+
+            OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
+
+                                           /*         v x (xj - xcn)          */
+            unitv(tmpvec, sjn);            /*  sjn = ----------------         */
+                                           /*        |v x (xj - xcn)|         */
+
+            sjn_rjn=iprod(sjn,rjn);        /* sjn_rjn = sjn . rjn             */
+
+
+            /*** A. Calculate the first of the four sum terms: ****************/
+            fac = OOpsij/OOpsijstar;
+            svmul(fac, rjn, tmpvec);
+            fac2 = fac*fac*OOpsij;
+            svmul(fac2*sjn_rjn, sjn, tmpvec2);
+            rvec_dec(tmpvec, tmpvec2);
+            fac2 = wj*gaussian_xj; /* also needed for sum4 */
+            svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
+            /********************/
+            /*** Add to sum1: ***/
+            /********************/
+            rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
+
+            /*** B. Calculate the forth of the four sum terms: ****************/
+            betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
+            /********************/
+            /*** Add to sum4: ***/
+            /********************/
+            slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
+            sum4 += slab_sum4part;
+
+            /*** C. Calculate Wjn for second and third sum */
+            /* Note that we can safely divide by slab_weights since we check in
+             * get_slab_centers that it is non-zero. */
+            Wjn = gaussian_xj*mj/erg->slab_weights[islab];
+
+            /* We already have precalculated the inner sum for slab n */
+            copy_rvec(erg->slab_innersumvec[islab], innersumvec);
+
+            /* Weigh the inner sum vector with Wjn */
+            svmul(Wjn, innersumvec, innersumvec);
+
+            /*** E. Calculate the second of the four sum terms: */
+            /********************/
+            /*** Add to sum2: ***/
+            /********************/
+            rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
+            
+            /*** F. Calculate the third of the four sum terms: */
+            slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
+            sum3 += slab_sum3part; /* still needs to be multiplied with v */
+
+            /*** G. Calculate the torque on the local slab's axis: */
+            if (bOutstepRot)
+            {
+                /* Sum1 */
+                cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
+                /* Sum2 */
+                cprod(innersumvec, rotg->vec, slab_sum2vec);
+                /* Sum3 */
+                svmul(slab_sum3part, rotg->vec, slab_sum3vec);
+                /* Sum4 */
+                svmul(slab_sum4part, rotg->vec, slab_sum4vec);
+
+                /* The force on atom ii from slab n only: */
+                for (m=0; m<DIM; m++)
+                    slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
+
+                erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
+            }
+        } /* END of loop over slabs */
+
+        /* Construct the four individual parts of the vector sum: */
+        cprod(sum1vec_part, rotg->vec, sum1vec);      /* sum1vec =   { } x v  */
+        cprod(sum2vec_part, rotg->vec, sum2vec);      /* sum2vec =   { } x v  */
+        svmul(sum3, rotg->vec, sum3vec);              /* sum3vec =   { } . v  */
+        svmul(sum4, rotg->vec, sum4vec);              /* sum4vec =   { } . v  */
+
+        /* Store the additional force so that it can be added to the force
+         * array after the normal forces have been evaluated */
+        for (m=0; m<DIM; m++)
+            erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
+
+#ifdef SUM_PARTS
+        fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n",    -rotg->k*sum1vec[XX],    -rotg->k*sum1vec[YY],    -rotg->k*sum1vec[ZZ]);
+        fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n",     rotg->k*sum2vec[XX],     rotg->k*sum2vec[YY],     rotg->k*sum2vec[ZZ]);
+        fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n",    -rotg->k*sum3vec[XX],    -rotg->k*sum3vec[YY],    -rotg->k*sum3vec[ZZ]);
+        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]);
+#endif
+
+        PRINT_FORCE_J
+
+    } /* END of loop over local atoms */
+
+    return V;
+}
+
+
+static real do_flex_lowlevel(
+        t_rotgrp *rotg,
+        real      sigma,     /* The Gaussian width sigma                      */
+        rvec      x[],
+        gmx_bool  bOutstepRot,
+        gmx_bool  bOutstepSlab,
+        matrix    box)
+{
+    int   count,ic,ifit,ii,j,m,n,islab,iigrp;
+    rvec  xj,yj0;            /* current and reference position                */
+    rvec  xcn, ycn;          /* the current and the reference slab centers    */
+    rvec  yj0_ycn;           /* yj0 - ycn                                     */
+    rvec  xj_xcn;            /* xj - xcn                                      */
+    rvec  qjn,fit_qjn;       /* q_i^n                                         */
+    rvec  sum_n1,sum_n2;     /* Two contributions to the rotation force       */
+    rvec  innersumvec;       /* Inner part of sum_n2                          */
+    rvec  s_n;
+    rvec  force_n;           /* Single force from slab n on one atom          */
+    rvec  force_n1,force_n2; /* First and second part of force_n              */
+    rvec  tmpvec,tmpvec2,tmp_f;   /* Helper variables                         */
+    real  V;                 /* The rotation potential energy                 */
+    real  OOsigma2;          /* 1/(sigma^2)                                   */
+    real  beta;              /* beta_n(xj)                                    */
+    real  bjn, fit_bjn;      /* b_j^n                                         */
+    real  gaussian_xj;       /* Gaussian weight gn(xj)                        */
+    real  betan_xj_sigma2;
+    real  mj,wj;             /* Mass-weighting of the positions               */
+    real  N_M;               /* N/M                                           */
+    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
+    gmx_bool bCalcPotFit;
+
+    
+    erg=rotg->enfrotgrp;
+
+    /* Pre-calculate the inner sums, so that we do not have to calculate
+     * them again for every atom */
+    flex_precalc_inner_sum(rotg);
+
+    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+    /********************************************************/
+    /* Main loop over all local atoms of the rotation group */
+    /********************************************************/
+    OOsigma2 = 1.0/(sigma*sigma);
+    N_M = rotg->nat * erg->invmass;
+    V = 0.0;
+    for (j=0; j<erg->nat_loc; j++)
+    {
+        /* Local index of a rotation group atom  */
+        ii = erg->ind_loc[j];
+        /* Position of this atom in the collective array */
+        iigrp = erg->xc_ref_ind[j];
+        /* Mass-weighting */
+        mj = erg->mc[iigrp];  /* need the unsorted mass here */
+        wj = N_M*mj;
+        
+        /* Current position of this atom: x[ii][XX/YY/ZZ]
+         * Note that erg->xc_center contains the center of mass in case the flex-t
+         * potential was chosen. For the flex potential erg->xc_center must be
+         * zero. */
+        rvec_sub(x[ii], erg->xc_center, xj);
+        
+        /* Shift this atom such that it is near its reference */
+        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+        /* Determine the slabs to loop over, i.e. the ones with contributions
+         * larger than min_gaussian */
+        count = get_single_atom_gaussians(xj, rotg);
+
+        clear_rvec(sum_n1);
+        clear_rvec(sum_n2);
+
+        /* Loop over the relevant slabs for this atom */
+        for (ic=0; ic < count; ic++)  
+        {
+            n = erg->gn_slabind[ic];
+                
+            /* Get the precomputed Gaussian for xj in slab n */
+            gaussian_xj = erg->gn_atom[ic];
+
+            islab = n - erg->slab_first; /* slab index */
+            
+            /* The (unrotated) reference position of this atom is saved in yj0: */
+            copy_rvec(rotg->x_ref[iigrp], yj0);
+
+            beta = calc_beta(xj, rotg, n);
+
+            /* The current center of this slab is saved in xcn: */
+            copy_rvec(erg->slab_center[islab], xcn);
+            /* ... and the reference center in ycn: */
+            copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+            
+            rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
+
+            /* Rotate: */
+            mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
+            
+            /* Subtract the slab center from xj */
+            rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
+            
+            /* Calculate qjn */
+            cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
+
+                                 /*         v x Omega.(yj0-ycn)    */
+            unitv(tmpvec,qjn);   /*  qjn = ---------------------   */
+                                 /*        |v x Omega.(yj0-ycn)|   */
+
+            bjn = iprod(qjn, xj_xcn);   /* bjn = qjn * (xj - xcn) */
+            
+            /*********************************/
+            /* Add to the rotation potential */
+            /*********************************/
+            V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
+            
+            /* If requested, also calculate the potential for a set of angles
+             * near the current reference angle */
+            if (bCalcPotFit)
+            {
+                for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+                {
+                    /* As above calculate Omega.(yj0-ycn), now for the other angles */
+                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
+                    /* As above calculate qjn */
+                    cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
+                                             /*             v x Omega.(yj0-ycn)    */
+                    unitv(tmpvec,fit_qjn);   /*  fit_qjn = ---------------------   */
+                                             /*            |v x Omega.(yj0-ycn)|   */
+                    fit_bjn = iprod(fit_qjn, xj_xcn);   /* fit_bjn = fit_qjn * (xj - xcn) */
+                    /* Add to the rotation potential for this angle */
+                    erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*sqr(fit_bjn);
+                }
+            }
+
+            /****************************************************************/
+            /* sum_n1 will typically be the main contribution to the force: */
+            /****************************************************************/
+            betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
+
+            /* The next lines calculate
+             *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
+            svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
+            rvec_sub(qjn,tmpvec2,tmpvec);
+
+            /* Multiply with gn(xj)*bjn: */
+            svmul(gaussian_xj*bjn,tmpvec,tmpvec2);
+
+            /* Sum over n: */
+            rvec_inc(sum_n1,tmpvec2);
+            
+            /* We already have precalculated the Sn term for slab n */
+            copy_rvec(erg->slab_innersumvec[islab], s_n);
+                                                                          /*          beta_n(xj)              */
+            svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
+                                                                          /*            sigma^2               */
+
+            rvec_sub(s_n, tmpvec, innersumvec);
+            
+            /* We can safely divide by slab_weights since we check in get_slab_centers
+             * that it is non-zero. */
+            svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
+
+            rvec_add(sum_n2, innersumvec, sum_n2);
+            
+            /* Calculate the torque: */
+            if (bOutstepRot)
+            {
+                /* The force on atom ii from slab n only: */
+                svmul(-rotg->k*wj, tmpvec2    , force_n1); /* part 1 */
+                svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
+                rvec_add(force_n1, force_n2, force_n);
+                erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
+            }
+        } /* END of loop over slabs */
+
+        /* Put both contributions together: */
+        svmul(wj, sum_n1, sum_n1);
+        svmul(mj, sum_n2, sum_n2);
+        rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
+
+        /* Store the additional force so that it can be added to the force
+         * array after the normal forces have been evaluated */
+        for(m=0; m<DIM; m++)
+            erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
+
+        PRINT_FORCE_J
+
+    } /* END of loop over local atoms */
+
+    return V;
+}
+
+#ifdef PRINT_COORDS
+static void print_coordinates(t_rotgrp *rotg, rvec x[], matrix box, int step)
+{
+    int i;
+    static FILE *fp;
+    static char buf[STRLEN];
+    static gmx_bool bFirst=1;
+
+
+    if (bFirst)
+    {
+        sprintf(buf, "coords%d.txt", cr->nodeid);
+        fp = fopen(buf, "w");
+        bFirst = 0;
+    }
+
+    fprintf(fp, "\nStep %d\n", step);
+    fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
+            box[XX][XX], box[XX][YY], box[XX][ZZ],
+            box[YY][XX], box[YY][YY], box[YY][ZZ],
+            box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
+    for (i=0; i<rotg->nat; i++)
+    {
+        fprintf(fp, "%4d  %f %f %f\n", i,
+                erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
+    }
+    fflush(fp);
+
+}
+#endif
+
+
+static int projection_compare(const void *a, const void *b)
+{
+    sort_along_vec_t *xca, *xcb;
+    
+    
+    xca = (sort_along_vec_t *)a;
+    xcb = (sort_along_vec_t *)b;
+    
+    if (xca->xcproj < xcb->xcproj)
+        return -1;
+    else if (xca->xcproj > xcb->xcproj)
+        return 1;
+    else
+        return 0;
+}
+
+
+static void sort_collective_coordinates(
+        t_rotgrp *rotg,         /* Rotation group */
+        sort_along_vec_t *data) /* Buffer for sorting the positions */
+{
+    int i;
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+
+    
+    erg=rotg->enfrotgrp;
+    
+    /* The projection of the position vector on the rotation vector is
+     * the relevant value for sorting. Fill the 'data' structure */
+    for (i=0; i<rotg->nat; i++)
+    {
+        data[i].xcproj = iprod(erg->xc[i], rotg->vec);  /* sort criterium */
+        data[i].m      = erg->mc[i];
+        data[i].ind    = i;
+        copy_rvec(erg->xc[i]    , data[i].x    );
+        copy_rvec(rotg->x_ref[i], data[i].x_ref);
+    }
+    /* Sort the 'data' structure */
+    gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
+    
+    /* Copy back the sorted values */
+    for (i=0; i<rotg->nat; i++)
+    {
+        copy_rvec(data[i].x    , erg->xc[i]           );
+        copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
+        erg->mc_sorted[i]  = data[i].m;
+        erg->xc_sortind[i] = data[i].ind;
+    }
+}
+
+
+/* For each slab, get the first and the last index of the sorted atom
+ * indices */
+static void get_firstlast_atom_per_slab(t_rotgrp *rotg)
+{
+    int i,islab,n;
+    real beta;
+    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
+
+    
+    erg=rotg->enfrotgrp;
+
+    /* Find the first atom that needs to enter the calculation for each slab */
+    n = erg->slab_first;  /* slab */
+    i = 0; /* start with the first atom */
+    do
+    {
+        /* Find the first atom that significantly contributes to this slab */
+        do /* move forward in position until a large enough beta is found */
+        {
+            beta = calc_beta(erg->xc[i], rotg, n);
+            i++;
+        } while ((beta < -erg->max_beta) && (i < rotg->nat));
+        i--;
+        islab = n - erg->slab_first;  /* slab index */
+        erg->firstatom[islab] = i;
+        /* Proceed to the next slab */
+        n++;
+    } while (n <= erg->slab_last);
+    
+    /* Find the last atom for each slab */
+     n = erg->slab_last; /* start with last slab */
+     i = rotg->nat-1;  /* start with the last atom */
+     do
+     {
+         do /* move backward in position until a large enough beta is found */
+         {
+             beta = calc_beta(erg->xc[i], rotg, n);
+             i--;
+         } while ((beta > erg->max_beta) && (i > -1));
+         i++;
+         islab = n - erg->slab_first;  /* slab index */
+         erg->lastatom[islab] = i;
+         /* Proceed to the next slab */
+         n--;
+     } while (n >= erg->slab_first);
+}
+
+
+/* Determine the very first and very last slab that needs to be considered 
+ * For the first slab that needs to be considered, we have to find the smallest
+ * n that obeys:
+ * 
+ * x_first * v - n*Delta_x <= beta_max
+ * 
+ * slab index n, slab distance Delta_x, rotation vector v. For the last slab we 
+ * have to find the largest n that obeys
+ * 
+ * x_last * v - n*Delta_x >= -beta_max
+ *  
+ */
+static gmx_inline int get_first_slab(
+        t_rotgrp *rotg,     /* The rotation group (inputrec data) */
+        real     max_beta,  /* The max_beta value, instead of min_gaussian */
+        rvec     firstatom) /* First atom after sorting along the rotation vector v */
+{
+    /* Find the first slab for the first atom */   
+    return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
+}
+
+
+static gmx_inline int get_last_slab(
+        t_rotgrp *rotg,     /* The rotation group (inputrec data) */
+        real     max_beta,  /* The max_beta value, instead of min_gaussian */
+        rvec     lastatom)  /* Last atom along v */
+{
+    /* Find the last slab for the last atom */
+    return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);    
+}
+
+
+static void get_firstlast_slab_check(
+        t_rotgrp        *rotg,     /* The rotation group (inputrec data) */
+        t_gmx_enfrotgrp *erg,      /* The rotation group (data only accessible in this file) */
+        rvec            firstatom, /* First atom after sorting along the rotation vector v */
+        rvec            lastatom,  /* Last atom along v */
+        int             g)         /* The rotation group number */
+{
+    erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
+    erg->slab_last  = get_last_slab(rotg, erg->max_beta, lastatom);
+
+    /* Check whether we have reference data to compare against */
+    if (erg->slab_first < erg->slab_first_ref)
+        gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
+                  RotStr, erg->slab_first);
+    
+    /* Check whether we have reference data to compare against */
+    if (erg->slab_last > erg->slab_last_ref)
+        gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
+                  RotStr, erg->slab_last);
+}
+
+
+/* Enforced rotation with a flexible axis */
+static void do_flexible(
+        gmx_bool  bMaster,
+        gmx_enfrot_t enfrot,    /* Other rotation data                        */
+        t_rotgrp  *rotg,        /* The rotation group                         */
+        int       g,            /* Group number                               */
+        rvec      x[],          /* The local positions                        */
+        matrix    box,
+        double    t,            /* Time in picoseconds                        */
+        gmx_large_int_t step,   /* The time step                              */
+        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
+        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
+{
+    int          l,nslabs;
+    real         sigma;       /* The Gaussian width sigma */
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+
+    
+    erg=rotg->enfrotgrp;
+
+    /* Define the sigma value */
+    sigma = 0.7*rotg->slab_dist;
+    
+    /* Sort the collective coordinates erg->xc along the rotation vector. This is
+     * an optimization for the inner loop. */
+    sort_collective_coordinates(rotg, enfrot->data);
+    
+    /* Determine the first relevant slab for the first atom and the last
+     * relevant slab for the last atom */
+    get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g);
+    
+    /* Determine for each slab depending on the min_gaussian cutoff criterium,
+     * a first and a last atom index inbetween stuff needs to be calculated */
+    get_firstlast_atom_per_slab(rotg);
+
+    /* Determine the gaussian-weighted center of positions for all slabs */
+    get_slab_centers(rotg,erg->xc,erg->mc_sorted,g,t,enfrot->out_slabs,bOutstepSlab,FALSE);
+        
+    /* Clear the torque per slab from last time step: */
+    nslabs = erg->slab_last - erg->slab_first + 1;
+    for (l=0; l<nslabs; l++)
+        erg->slab_torque_v[l] = 0.0;
+    
+    /* Call the rotational forces kernel */
+    if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
+        erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
+    else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
+        erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
+    else
+        gmx_fatal(FARGS, "Unknown flexible rotation type");
+    
+    /* Determine angle by RMSD fit to the reference - Let's hope this */
+    /* only happens once in a while, since this is not parallelized! */
+    if ( bMaster && (erotgFitPOT != rotg->eFittype) )
+    {
+        if (bOutstepRot)
+        {
+            /* Fit angle of the whole rotation group */
+            erg->angle_v = flex_fit_angle(rotg);
+        }
+        if (bOutstepSlab)
+        {
+            /* Fit angle of each slab */
+            flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
+        }
+    }
+
+    /* Lump together the torques from all slabs: */
+    erg->torque_v = 0.0;
+    for (l=0; l<nslabs; l++)
+         erg->torque_v += erg->slab_torque_v[l];
+}
+
+
+/* Calculate the angle between reference and actual rotation group atom,
+ * both projected into a plane perpendicular to the rotation vector: */
+static void angle(t_rotgrp *rotg,
+        rvec x_act,
+        rvec x_ref,
+        real *alpha,
+        real *weight)  /* atoms near the rotation axis should count less than atoms far away */
+{
+    rvec xp, xrp;  /* current and reference positions projected on a plane perpendicular to pg->vec */
+    rvec dum;
+
+
+    /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
+    /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
+    svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
+    rvec_sub(x_ref, dum, xrp);
+    /* Project x_act: */
+    svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
+    rvec_sub(x_act, dum, xp);
+
+    /* Retrieve information about which vector precedes. gmx_angle always
+     * returns a positive angle. */
+    cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
+
+    if (iprod(rotg->vec, dum) >= 0)
+        *alpha = -gmx_angle(xrp, xp);
+    else
+        *alpha = +gmx_angle(xrp, xp);
+    
+    /* Also return the weight */
+    *weight = norm(xp);
+}
+
+
+/* Project first vector onto a plane perpendicular to the second vector 
+ * dr = dr - (dr.v)v
+ * Note that v must be of unit length.
+ */
+static gmx_inline void project_onto_plane(rvec dr, const rvec v)
+{
+    rvec tmp;
+    
+    
+    svmul(iprod(dr,v),v,tmp);  /* tmp = (dr.v)v */
+    rvec_dec(dr, tmp);         /* dr = dr - (dr.v)v */
+}
+
+
+/* Fixed rotation: The rotation reference group rotates around the v axis. */
+/* The atoms of the actual rotation group are attached with imaginary  */
+/* springs to the reference atoms.                                     */
+static void do_fixed(
+        t_rotgrp  *rotg,        /* The rotation group                         */
+        rvec      x[],          /* The positions                              */
+        matrix    box,          /* The simulation box                         */
+        double    t,            /* Time in picoseconds                        */
+        gmx_large_int_t step,   /* The time step                              */
+        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
+        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
+{
+    int       ifit,j,jj,m;
+    rvec      dr;
+    rvec      tmp_f;           /* Force */
+    real      alpha;           /* a single angle between an actual and a reference position */
+    real      weight;          /* single weight for a single angle */
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    rvec      xi_xc;           /* xi - xc */
+    gmx_bool  bCalcPotFit;
+    rvec      fit_xr_loc;
+
+    /* for mass weighting: */
+    real      wi;              /* Mass-weighting of the positions */
+    real      N_M;             /* N/M */
+    real      k_wi;            /* k times wi */
+
+    gmx_bool  bProject;
+
+    
+    erg=rotg->enfrotgrp;
+    bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
+    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+    N_M = rotg->nat * erg->invmass;
+
+    /* Each process calculates the forces on its local atoms */
+    for (j=0; j<erg->nat_loc; j++)
+    {
+        /* Calculate (x_i-x_c) resp. (x_i-u) */
+        rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
+
+        /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+        rvec_sub(erg->xr_loc[j], xi_xc, dr);
+        
+        if (bProject)
+            project_onto_plane(dr, rotg->vec);
+            
+        /* Mass-weighting */
+        wi = N_M*erg->m_loc[j];
+
+        /* Store the additional force so that it can be added to the force
+         * array after the normal forces have been evaluated */
+        k_wi = rotg->k*wi;
+        for (m=0; m<DIM; m++)
+        {
+            tmp_f[m]             = k_wi*dr[m];
+            erg->f_rot_loc[j][m] = tmp_f[m];
+            erg->V              += 0.5*k_wi*sqr(dr[m]);
+        }
+        
+        /* If requested, also calculate the potential for a set of angles
+         * near the current reference angle */
+        if (bCalcPotFit)
+        {
+            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+            {
+                /* Index of this rotation group atom with respect to the whole rotation group */
+                jj = erg->xc_ref_ind[j];
+
+                /* Rotate with the alternative angle. Like rotate_local_reference(),
+                 * just for a single local atom */
+                mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
+
+                /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+                rvec_sub(fit_xr_loc, xi_xc, dr);
+
+                if (bProject)
+                    project_onto_plane(dr, rotg->vec);
+
+                /* Add to the rotation potential for this angle: */
+                erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
+            }
+        }
+
+        if (bOutstepRot)
+        {
+            /* Add to the torque of this rotation group */
+            erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+            
+            /* Calculate the angle between reference and actual rotation group atom. */
+            angle(rotg, xi_xc, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
+            erg->angle_v  += alpha * weight;
+            erg->weight_v += weight;
+        }
+        /* If you want enforced rotation to contribute to the virial,
+         * activate the following lines:
+            if (MASTER(cr))
+            {
+               Add the rotation contribution to the virial
+              for(j=0; j<DIM; j++)
+                for(m=0;m<DIM;m++)
+                  vir[j][m] += 0.5*f[ii][j]*dr[m];
+            }
+         */
+
+        PRINT_FORCE_J
+
+    } /* end of loop over local rotation group atoms */
+}
+
+
+/* Calculate the radial motion potential and forces */
+static void do_radial_motion(
+        t_rotgrp  *rotg,        /* The rotation group                         */
+        rvec      x[],          /* The positions                              */
+        matrix    box,          /* The simulation box                         */
+        double    t,            /* Time in picoseconds                        */
+        gmx_large_int_t step,   /* The time step                              */
+        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
+        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
+{
+    int       j,jj,ifit;
+    rvec      tmp_f;           /* Force */
+    real      alpha;           /* a single angle between an actual and a reference position */
+    real      weight;          /* single weight for a single angle */
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    rvec      xj_u;            /* xj - u */
+    rvec      tmpvec,fit_tmpvec;
+    real      fac,fac2,sum=0.0;
+    rvec      pj;
+    gmx_bool  bCalcPotFit;
+
+    /* For mass weighting: */
+    real      wj;              /* Mass-weighting of the positions */
+    real      N_M;             /* N/M */
+
+
+    erg=rotg->enfrotgrp;
+    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+    N_M = rotg->nat * erg->invmass;
+
+    /* Each process calculates the forces on its local atoms */
+    for (j=0; j<erg->nat_loc; j++)
+    {
+        /* Calculate (xj-u) */
+        rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
+
+        /* Calculate Omega.(yj0-u) */
+        cprod(rotg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
+
+                              /*         v x Omega.(yj0-u)     */
+        unitv(tmpvec, pj);    /*  pj = ---------------------   */
+                              /*       | v x Omega.(yj0-u) |   */
+
+        fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
+        fac2 = fac*fac;
+
+        /* Mass-weighting */
+        wj = N_M*erg->m_loc[j];
+
+        /* Store the additional force so that it can be added to the force
+         * array after the normal forces have been evaluated */
+        svmul(-rotg->k*wj*fac, pj, tmp_f);
+        copy_rvec(tmp_f, erg->f_rot_loc[j]);
+        sum += wj*fac2;
+
+        /* If requested, also calculate the potential for a set of angles
+         * near the current reference angle */
+        if (bCalcPotFit)
+        {
+            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+            {
+                /* Index of this rotation group atom with respect to the whole rotation group */
+                jj = erg->xc_ref_ind[j];
+
+                /* Rotate with the alternative angle. Like rotate_local_reference(),
+                 * just for a single local atom */
+                mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
+
+                /* Calculate Omega.(yj0-u) */
+                cprod(rotg->vec, fit_tmpvec, tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
+                                      /*         v x Omega.(yj0-u)     */
+                unitv(tmpvec, pj);    /*  pj = ---------------------   */
+                                      /*       | v x Omega.(yj0-u) |   */
+
+                fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
+                fac2 = fac*fac;
+
+                /* Add to the rotation potential for this angle: */
+                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
+            }
+        }
+
+        if (bOutstepRot)
+        {
+            /* Add to the torque of this rotation group */
+            erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+            /* Calculate the angle between reference and actual rotation group atom. */
+            angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
+            erg->angle_v  += alpha * weight;
+            erg->weight_v += weight;
+        }
+
+        PRINT_FORCE_J
+
+    } /* end of loop over local rotation group atoms */
+    erg->V = 0.5*rotg->k*sum;
+}
+
+
+/* Calculate the radial motion pivot-free potential and forces */
+static void do_radial_motion_pf(
+        t_rotgrp  *rotg,        /* The rotation group                         */
+        rvec      x[],          /* The positions                              */
+        matrix    box,          /* The simulation box                         */
+        double    t,            /* Time in picoseconds                        */
+        gmx_large_int_t step,   /* The time step                              */
+        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
+        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
+{
+    int       i,ii,iigrp,ifit,j;
+    rvec      xj;              /* Current position */
+    rvec      xj_xc;           /* xj  - xc  */
+    rvec      yj0_yc0;         /* yj0 - yc0 */
+    rvec      tmp_f;           /* Force */
+    real      alpha;           /* a single angle between an actual and a reference position */
+    real      weight;          /* single weight for a single angle */
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    rvec      tmpvec, tmpvec2;
+    rvec      innersumvec;     /* Precalculation of the inner sum */
+    rvec      innersumveckM;
+    real      fac,fac2,V=0.0;
+    rvec      qi,qj;
+    gmx_bool  bCalcPotFit;
+
+    /* For mass weighting: */
+    real      mj,wi,wj;        /* Mass-weighting of the positions */
+    real      N_M;             /* N/M */
+
+
+    erg=rotg->enfrotgrp;
+    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+    N_M = rotg->nat * erg->invmass;
+
+    /* Get the current center of the rotation group: */
+    get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+    /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
+    clear_rvec(innersumvec);
+    for (i=0; i < rotg->nat; i++)
+    {
+        /* Mass-weighting */
+        wi = N_M*erg->mc[i];
+
+        /* Calculate qi. Note that xc_ref_center has already been subtracted from
+         * x_ref in init_rot_group.*/
+        mvmul(erg->rotmat, rotg->x_ref[i], tmpvec);  /* tmpvec  = Omega.(yi0-yc0) */
+
+        cprod(rotg->vec, tmpvec, tmpvec2);          /* tmpvec2 = v x Omega.(yi0-yc0) */
+
+                              /*         v x Omega.(yi0-yc0)     */
+        unitv(tmpvec2, qi);   /*  qi = -----------------------   */
+                              /*       | v x Omega.(yi0-yc0) |   */
+
+        rvec_sub(erg->xc[i], erg->xc_center, tmpvec);  /* tmpvec = xi-xc */
+
+        svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
+
+        rvec_inc(innersumvec, tmpvec2);
+    }
+    svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
+
+    /* Each process calculates the forces on its local atoms */
+    for (j=0; j<erg->nat_loc; j++)
+    {
+        /* Local index of a rotation group atom  */
+        ii = erg->ind_loc[j];
+        /* Position of this atom in the collective array */
+        iigrp = erg->xc_ref_ind[j];
+        /* Mass-weighting */
+        mj = erg->mc[iigrp];  /* need the unsorted mass here */
+        wj = N_M*mj;
+
+        /* Current position of this atom: x[ii][XX/YY/ZZ] */
+        copy_rvec(x[ii], xj);
+
+        /* Shift this atom such that it is near its reference */
+        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+        /* The (unrotated) reference position is yj0. yc0 has already
+         * been subtracted in init_rot_group */
+        copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
+
+        /* Calculate Omega.(yj0-yc0) */
+        mvmul(erg->rotmat, yj0_yc0, tmpvec2);     /* tmpvec2 = Omega.(yj0 - yc0)  */
+
+        cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
+
+                              /*         v x Omega.(yj0-yc0)     */
+        unitv(tmpvec, qj);    /*  qj = -----------------------   */
+                              /*       | v x Omega.(yj0-yc0) |   */
+
+        /* Calculate (xj-xc) */
+        rvec_sub(xj, erg->xc_center, xj_xc);  /* xj_xc = xj-xc */
+
+        fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
+        fac2 = fac*fac;
+
+        /* Store the additional force so that it can be added to the force
+         * array after the normal forces have been evaluated */
+        svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
+        svmul(mj, innersumveckM, tmpvec);  /* part 2 of force */
+        rvec_inc(tmp_f, tmpvec);
+        copy_rvec(tmp_f, erg->f_rot_loc[j]);
+        V += wj*fac2;
+
+        /* If requested, also calculate the potential for a set of angles
+         * near the current reference angle */
+        if (bCalcPotFit)
+        {
+            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+            {
+                /* Rotate with the alternative angle. Like rotate_local_reference(),
+                 * just for a single local atom */
+                mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
+
+                /* Calculate Omega.(yj0-u) */
+                cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
+                                      /*         v x Omega.(yj0-yc0)     */
+                unitv(tmpvec, qj);    /*  qj = -----------------------   */
+                                      /*       | v x Omega.(yj0-yc0) |   */
+
+                fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
+                fac2 = fac*fac;
+
+                /* Add to the rotation potential for this angle: */
+                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
+            }
+        }
+
+        if (bOutstepRot)
+        {
+            /* Add to the torque of this rotation group */
+            erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
+
+            /* Calculate the angle between reference and actual rotation group atom. */
+            angle(rotg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
+            erg->angle_v  += alpha * weight;
+            erg->weight_v += weight;
+        }
+
+        PRINT_FORCE_J
+
+    } /* end of loop over local rotation group atoms */
+    erg->V = 0.5*rotg->k*V;
+}
+
+
+/* Precalculate the inner sum for the radial motion 2 forces */
+static void radial_motion2_precalc_inner_sum(t_rotgrp  *rotg, rvec innersumvec)
+{
+    int       i;
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    rvec      xi_xc;           /* xj - xc */
+    rvec      tmpvec,tmpvec2;
+    real      fac,fac2;
+    rvec      ri,si;
+    real      siri;
+    rvec      v_xi_xc;          /* v x (xj - u) */
+    real      psii,psiistar;
+    real      wi;              /* Mass-weighting of the positions */
+    real      N_M;             /* N/M */
+    rvec      sumvec;
+
+    erg=rotg->enfrotgrp;
+    N_M = rotg->nat * erg->invmass;
+
+    /* Loop over the collective set of positions */
+    clear_rvec(sumvec);
+    for (i=0; i<rotg->nat; i++)
+    {
+        /* Mass-weighting */
+        wi = N_M*erg->mc[i];
+
+        rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
+
+        /* Calculate ri. Note that xc_ref_center has already been subtracted from
+         * x_ref in init_rot_group.*/
+        mvmul(erg->rotmat, rotg->x_ref[i], ri);      /* ri  = Omega.(yi0-yc0) */
+
+        cprod(rotg->vec, xi_xc, v_xi_xc);            /* v_xi_xc = v x (xi-u)  */
+
+        fac = norm2(v_xi_xc);
+                                          /*                      1           */
+        psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
+                                          /*            |v x (xi-xc)|^2 + eps */
+
+        psii = gmx_invsqrt(fac);          /*                 1                */
+                                          /*  psii    = -------------         */
+                                          /*            |v x (xi-xc)|         */
+
+        svmul(psii, v_xi_xc, si);          /*  si = psii * (v x (xi-xc) )     */
+
+        fac = iprod(v_xi_xc, ri);                   /* fac = (v x (xi-xc)).ri */
+        fac2 = fac*fac;
+
+        siri = iprod(si, ri);                       /* siri = si.ri           */
+
+        svmul(psiistar/psii, ri, tmpvec);
+        svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
+        rvec_dec(tmpvec, tmpvec2);
+        cprod(tmpvec, rotg->vec, tmpvec2);
+
+        svmul(wi*siri, tmpvec2, tmpvec);
+
+        rvec_inc(sumvec, tmpvec);
+    }
+    svmul(rotg->k*erg->invmass, sumvec, innersumvec);
+}
+
+
+/* Calculate the radial motion 2 potential and forces */
+static void do_radial_motion2(
+        t_rotgrp  *rotg,        /* The rotation group                         */
+        rvec      x[],          /* The positions                              */
+        matrix    box,          /* The simulation box                         */
+        double    t,            /* Time in picoseconds                        */
+        gmx_large_int_t step,   /* The time step                              */
+        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
+        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
+{
+    int       ii,iigrp,ifit,j;
+    rvec      xj;              /* Position */
+    real      alpha;           /* a single angle between an actual and a reference position */
+    real      weight;          /* single weight for a single angle */
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    rvec      xj_u;            /* xj - u */
+    rvec      yj0_yc0;         /* yj0 -yc0 */
+    rvec      tmpvec,tmpvec2;
+    real      fac,fit_fac,fac2,Vpart=0.0;
+    rvec      rj,fit_rj,sj;
+    real      sjrj;
+    rvec      v_xj_u;          /* v x (xj - u) */
+    real      psij,psijstar;
+    real      mj,wj;           /* For mass-weighting of the positions */
+    real      N_M;             /* N/M */
+    gmx_bool  bPF;
+    rvec      innersumvec;
+    gmx_bool  bCalcPotFit;
+
+
+    erg=rotg->enfrotgrp;
+
+    bPF = rotg->eType==erotgRM2PF;
+    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+
+    clear_rvec(yj0_yc0); /* Make the compiler happy */
+
+    clear_rvec(innersumvec);
+    if (bPF)
+    {
+        /* For the pivot-free variant we have to use the current center of
+         * mass of the rotation group instead of the pivot u */
+        get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+        /* Also, we precalculate the second term of the forces that is identical
+         * (up to the weight factor mj) for all forces */
+        radial_motion2_precalc_inner_sum(rotg,innersumvec);
+    }
+
+    N_M = rotg->nat * erg->invmass;
+
+    /* Each process calculates the forces on its local atoms */
+    for (j=0; j<erg->nat_loc; j++)
+    {
+        if (bPF)
+        {
+            /* Local index of a rotation group atom  */
+            ii = erg->ind_loc[j];
+            /* Position of this atom in the collective array */
+            iigrp = erg->xc_ref_ind[j];
+            /* Mass-weighting */
+            mj = erg->mc[iigrp];
+
+            /* Current position of this atom: x[ii] */
+            copy_rvec(x[ii], xj);
+
+            /* Shift this atom such that it is near its reference */
+            shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+            /* The (unrotated) reference position is yj0. yc0 has already
+             * been subtracted in init_rot_group */
+            copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0  */
+
+            /* Calculate Omega.(yj0-yc0) */
+            mvmul(erg->rotmat, yj0_yc0, rj);         /* rj = Omega.(yj0-yc0)  */
+        }
+        else
+        {
+            mj = erg->m_loc[j];
+            copy_rvec(erg->x_loc_pbc[j], xj);
+            copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
+        }
+        /* Mass-weighting */
+        wj = N_M*mj;
+
+        /* Calculate (xj-u) resp. (xj-xc) */
+        rvec_sub(xj, erg->xc_center, xj_u);          /* xj_u = xj-u           */
+
+        cprod(rotg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
+
+        fac = norm2(v_xj_u);
+                                          /*                      1           */
+        psijstar = 1.0/(fac + rotg->eps); /*  psistar = --------------------  */
+                                          /*            |v x (xj-u)|^2 + eps  */
+
+        psij = gmx_invsqrt(fac);          /*                 1                */
+                                          /*  psij    = ------------          */
+                                          /*            |v x (xj-u)|          */
+
+        svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
+
+        fac = iprod(v_xj_u, rj);                     /* fac = (v x (xj-u)).rj */
+        fac2 = fac*fac;
+
+        sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
+
+        svmul(psijstar/psij, rj, tmpvec);
+        svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
+        rvec_dec(tmpvec, tmpvec2);
+        cprod(tmpvec, rotg->vec, tmpvec2);
+
+        /* Store the additional force so that it can be added to the force
+         * array after the normal forces have been evaluated */
+        svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
+        svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
+
+        rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
+        Vpart += wj*psijstar*fac2;
+
+        /* If requested, also calculate the potential for a set of angles
+         * near the current reference angle */
+        if (bCalcPotFit)
+        {
+            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+            {
+                if (bPF)
+                {
+                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
+                }
+                else
+                {
+                    /* Position of this atom in the collective array */
+                    iigrp = erg->xc_ref_ind[j];
+                    /* Rotate with the alternative angle. Like rotate_local_reference(),
+                     * just for a single local atom */
+                    mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
+                }
+                fit_fac = iprod(v_xj_u, fit_rj); /* fac = (v x (xj-u)).fit_rj */
+                /* Add to the rotation potential for this angle: */
+                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*psijstar*fit_fac*fit_fac;
+            }
+        }
+
+        if (bOutstepRot)
+        {
+            /* Add to the torque of this rotation group */
+            erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
+
+            /* Calculate the angle between reference and actual rotation group atom. */
+            angle(rotg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
+            erg->angle_v  += alpha * weight;
+            erg->weight_v += weight;
+        }
+
+        PRINT_FORCE_J
+
+    } /* end of loop over local rotation group atoms */
+    erg->V = 0.5*rotg->k*Vpart;
+}
+
+
+/* Determine the smallest and largest position vector (with respect to the 
+ * rotation vector) for the reference group */
+static void get_firstlast_atom_ref(
+        t_rotgrp  *rotg, 
+        int       *firstindex, 
+        int       *lastindex)
+{
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    int i;
+    real xcproj;               /* The projection of a reference position on the 
+                                  rotation vector */
+    real minproj, maxproj;     /* Smallest and largest projection on v */
+    
+
+    
+    erg=rotg->enfrotgrp;
+
+    /* Start with some value */
+    minproj = iprod(rotg->x_ref[0], rotg->vec);
+    maxproj = minproj;
+    
+    /* This is just to ensure that it still works if all the atoms of the 
+     * reference structure are situated in a plane perpendicular to the rotation 
+     * vector */
+    *firstindex = 0;
+    *lastindex  = rotg->nat-1;
+    
+    /* Loop over all atoms of the reference group, 
+     * project them on the rotation vector to find the extremes */
+    for (i=0; i<rotg->nat; i++)
+    {
+        xcproj = iprod(rotg->x_ref[i], rotg->vec);
+        if (xcproj < minproj)
+        {
+            minproj = xcproj;
+            *firstindex = i;
+        }
+        if (xcproj > maxproj)
+        {
+            maxproj = xcproj;
+            *lastindex = i;
+        }
+    }
+}
+
+
+/* Allocate memory for the slabs */
+static void allocate_slabs(
+        t_rotgrp  *rotg, 
+        FILE      *fplog, 
+        int       g, 
+        gmx_bool  bVerbose)
+{
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+    int i, nslabs;
+    
+    
+    erg=rotg->enfrotgrp;
+    
+    /* More slabs than are defined for the reference are never needed */
+    nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
+    
+    /* Remember how many we allocated */
+    erg->nslabs_alloc = nslabs;
+
+    if ( (NULL != fplog) && bVerbose )
+        fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
+                RotStr, nslabs,g);
+    snew(erg->slab_center     , nslabs);
+    snew(erg->slab_center_ref , nslabs);
+    snew(erg->slab_weights    , nslabs);
+    snew(erg->slab_torque_v   , nslabs);
+    snew(erg->slab_data       , nslabs);
+    snew(erg->gn_atom         , nslabs);
+    snew(erg->gn_slabind      , nslabs);
+    snew(erg->slab_innersumvec, nslabs);
+    for (i=0; i<nslabs; i++)
+    {
+        snew(erg->slab_data[i].x     , rotg->nat);
+        snew(erg->slab_data[i].ref   , rotg->nat);
+        snew(erg->slab_data[i].weight, rotg->nat);
+    }
+    snew(erg->xc_ref_sorted, rotg->nat);
+    snew(erg->xc_sortind   , rotg->nat);
+    snew(erg->firstatom    , nslabs);
+    snew(erg->lastatom     , nslabs);
+}
+
+
+/* From the extreme coordinates of the reference group, determine the first 
+ * and last slab of the reference. We can never have more slabs in the real
+ * simulation than calculated here for the reference.
+ */
+static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
+{
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+    int first,last,firststart;
+    rvec dummy;
+
+    
+    erg=rotg->enfrotgrp;
+    first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
+    last  = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
+    firststart = first;
+
+    while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+    {
+        first--;
+    }
+    erg->slab_first_ref = first+1;
+    while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+    {
+        last++;
+    }
+    erg->slab_last_ref  = last-1;
+    
+    erg->slab_buffer = firststart - erg->slab_first_ref;
+}
+
+
+
+static void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
+        rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs, gmx_bool bOutputCenters)
+{
+    int i,ii;
+    rvec        coord,*xdum;
+    gmx_bool    bFlex,bColl;
+    t_atom      *atom;
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+    int         ref_firstindex, ref_lastindex;
+    real        mass,totalmass;
+    real        start=0.0;
+    
+
+    /* Do we have a flexible axis? */
+    bFlex = ISFLEX(rotg);
+    /* Do we use a global set of coordinates? */
+    bColl = ISCOLL(rotg);
+
+    erg=rotg->enfrotgrp;
+    
+    /* Allocate space for collective coordinates if needed */
+    if (bColl)
+    {
+        snew(erg->xc        , rotg->nat);
+        snew(erg->xc_shifts , rotg->nat);
+        snew(erg->xc_eshifts, rotg->nat);
+
+        /* Save the original (whole) set of positions such that later the
+         * molecule can always be made whole again */
+        snew(erg->xc_old    , rotg->nat);        
+        if (MASTER(cr))
+        {
+            for (i=0; i<rotg->nat; i++)
+            {
+                ii = rotg->ind[i];
+                copy_rvec(x[ii], erg->xc_old[i]);
+            }
+        }
+#ifdef GMX_MPI
+        if (PAR(cr))
+            gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
+#endif
+
+        if (rotg->eFittype == erotgFitNORM)
+        {
+            snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
+            snew(erg->xc_norm      , rotg->nat);
+        }
+    }
+    else
+    {
+        snew(erg->xr_loc   , rotg->nat);
+        snew(erg->x_loc_pbc, rotg->nat);
+    }
+    
+    snew(erg->f_rot_loc , rotg->nat);
+    snew(erg->xc_ref_ind, rotg->nat);
+    
+    /* Make space for the calculation of the potential at other angles (used
+     * for fitting only) */
+    if (erotgFitPOT == rotg->eFittype)
+    {
+        snew(erg->PotAngleFit, 1);
+        snew(erg->PotAngleFit->degangle, rotg->PotAngle_nstep);
+        snew(erg->PotAngleFit->V       , rotg->PotAngle_nstep);
+        snew(erg->PotAngleFit->rotmat  , rotg->PotAngle_nstep);
+
+        /* Get the set of angles around the reference angle */
+        start = -0.5 * (rotg->PotAngle_nstep - 1)*rotg->PotAngle_step;
+        for (i = 0; i < rotg->PotAngle_nstep; i++)
+            erg->PotAngleFit->degangle[i] = start + i*rotg->PotAngle_step;
+    }
+    else
+    {
+        erg->PotAngleFit = NULL;
+    }
+
+    /* xc_ref_ind needs to be set to identity in the serial case */
+    if (!PAR(cr))
+        for (i=0; i<rotg->nat; i++)
+            erg->xc_ref_ind[i] = i;
+
+    /* Copy the masses so that the center can be determined. For all types of
+     * enforced rotation, we store the masses in the erg->mc array. */
+    snew(erg->mc, rotg->nat);
+    if (bFlex)
+        snew(erg->mc_sorted, rotg->nat);
+    if (!bColl)
+        snew(erg->m_loc, rotg->nat);
+    totalmass=0.0;
+    for (i=0; i<rotg->nat; i++)
+    {
+        if (rotg->bMassW)
+        {
+            gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
+            mass=atom->m;
+        }
+        else
+        {
+            mass=1.0;
+        }
+        erg->mc[i] = mass;
+        totalmass += mass;
+    }
+    erg->invmass = 1.0/totalmass;
+    
+    /* Set xc_ref_center for any rotation potential */
+    if ((rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2))
+    {
+        /* Set the pivot point for the fixed, stationary-axis potentials. This
+         * won't change during the simulation */
+        copy_rvec(rotg->pivot, erg->xc_ref_center);
+        copy_rvec(rotg->pivot, erg->xc_center    );
+    }
+    else
+    {
+        /* Center of the reference positions */
+        get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
+
+        /* Center of the actual positions */
+        if (MASTER(cr))
+        {
+            snew(xdum, rotg->nat);
+            for (i=0; i<rotg->nat; i++)
+            {
+                ii = rotg->ind[i];
+                copy_rvec(x[ii], xdum[i]);
+            }
+            get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
+            sfree(xdum);
+        }
+#ifdef GMX_MPI
+        if (PAR(cr))
+            gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
+#endif
+    }
+
+    if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
+    {
+        /* Put the reference positions into origin: */
+        for (i=0; i<rotg->nat; i++)
+            rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
+    }
+
+    /* Enforced rotation with flexible axis */
+    if (bFlex)
+    {
+        /* Calculate maximum beta value from minimum gaussian (performance opt.) */
+        erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
+
+        /* Determine the smallest and largest coordinate with respect to the rotation vector */
+        get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
+        
+        /* From the extreme coordinates of the reference group, determine the first 
+         * and last slab of the reference. */
+        get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
+                
+        /* Allocate memory for the slabs */
+        allocate_slabs(rotg, fplog, g, bVerbose);
+
+        /* Flexible rotation: determine the reference centers for the rest of the simulation */
+        erg->slab_first = erg->slab_first_ref;
+        erg->slab_last = erg->slab_last_ref;
+        get_slab_centers(rotg,rotg->x_ref,erg->mc,g,-1,out_slabs,bOutputCenters,TRUE);
+
+        /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
+        if (rotg->eFittype == erotgFitNORM)
+        {
+            for (i=0; i<rotg->nat; i++)
+            {
+                rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
+                erg->xc_ref_length[i] = norm(coord);
+            }
+        }
+    }
+}
+
+
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot)
+{
+    gmx_ga2la_t ga2la;
+    int g;
+    t_rotgrp *rotg;
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+    
+    ga2la = dd->ga2la;
+
+    for(g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        erg  = rotg->enfrotgrp;
+
+
+        dd_make_local_group_indices(ga2la,rotg->nat,rotg->ind,
+                &erg->nat_loc,&erg->ind_loc,&erg->nalloc_loc,erg->xc_ref_ind);
+    }
+}
+
+
+/* Calculate the size of the MPI buffer needed in reduce_output() */
+static int calc_mpi_bufsize(t_rot *rot)
+{
+    int g;
+    int count_group, count_total;
+    t_rotgrp *rotg;
+    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
+
+
+    count_total = 0;
+    for (g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        erg  = rotg->enfrotgrp;
+
+        /* Count the items that are transferred for this group: */
+        count_group = 4; /* V, torque, angle, weight */
+
+        /* Add the maximum number of slabs for flexible groups */
+        if (ISFLEX(rotg))
+            count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
+
+        /* Add space for the potentials at different angles: */
+        if (erotgFitPOT == rotg->eFittype)
+            count_group += rotg->PotAngle_nstep;
+
+        /* Add to the total number: */
+        count_total += count_group;
+    }
+
+    return count_total;
+}
+
+
+extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
+        t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+        gmx_bool bVerbose, unsigned long Flags)
+{
+    t_rot    *rot;
+    t_rotgrp *rotg;
+    int      g;
+    int      nat_max=0;     /* Size of biggest rotation group */
+    gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
+    gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
+    rvec     *x_pbc=NULL;   /* Space for the pbc-correct atom positions */
+
+
+    if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
+        gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
+
+    if ( MASTER(cr) && bVerbose)
+        fprintf(stdout, "%s Initializing ...\n", RotStr);
+
+    rot = ir->rot;
+    snew(rot->enfrot, 1);
+    er = rot->enfrot;
+    er->Flags = Flags;
+
+    /* When appending, skip first output to avoid duplicate entries in the data files */
+    if (er->Flags & MD_APPENDFILES)
+        er->bOut = FALSE;
+    else
+        er->bOut = TRUE;
+
+    if ( MASTER(cr) && er->bOut )
+        please_cite(fplog, "Kutzner2011");
+
+    /* Output every step for reruns */
+    if (er->Flags & MD_RERUN)
+    {
+        if (NULL != fplog)
+            fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
+        rot->nstrout = 1;
+        rot->nstsout = 1;
+    }
+
+    er->out_slabs = NULL;
+    if ( MASTER(cr) && HaveFlexibleGroups(rot) )
+        er->out_slabs = open_slab_out(opt2fn("-rs",nfile,fnm), rot, oenv);
+
+    if (MASTER(cr))
+    {
+        /* Remove pbc, make molecule whole.
+         * When ir->bContinuation=TRUE this has already been done, but ok. */
+        snew(x_pbc,mtop->natoms);
+        m_rveccopy(mtop->natoms,x,x_pbc);
+        do_pbc_first_mtop(NULL,ir->ePBC,box,mtop,x_pbc);
+    }
+
+    for (g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+
+        if (NULL != fplog)
+            fprintf(fplog,"%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
+        
+        if (rotg->nat > 0)
+        {
+            /* Allocate space for the rotation group's data: */
+            snew(rotg->enfrotgrp, 1);
+            erg  = rotg->enfrotgrp;
+
+            nat_max=max(nat_max, rotg->nat);
+            
+            if (PAR(cr))
+            {
+                erg->nat_loc    = 0;
+                erg->nalloc_loc = 0;
+                erg->ind_loc    = NULL;
+            }
+            else
+            {
+                erg->nat_loc = rotg->nat;
+                erg->ind_loc = rotg->ind;
+            }
+            init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs,
+                           !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
+                                                             * again if we are appending */
+        }
+    }
+    
+    /* Allocate space for enforced rotation buffer variables */
+    er->bufsize = nat_max;
+    snew(er->data, nat_max);
+    snew(er->xbuf, nat_max);
+    snew(er->mbuf, nat_max);
+
+    /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
+    if (PAR(cr))
+    {
+        er->mpi_bufsize = calc_mpi_bufsize(rot) + 100; /* larger to catch errors */
+        snew(er->mpi_inbuf , er->mpi_bufsize);
+        snew(er->mpi_outbuf, er->mpi_bufsize);
+    }
+    else
+    {
+        er->mpi_bufsize = 0;
+        er->mpi_inbuf = NULL;
+        er->mpi_outbuf = NULL;
+    }
+
+    /* Only do I/O on the MASTER */
+    er->out_angles  = NULL;
+    er->out_rot     = NULL;
+    er->out_torque  = NULL;
+    if (MASTER(cr))
+    {
+        er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv);
+
+        if (rot->nstsout > 0)
+        {
+            if ( HaveFlexibleGroups(rot) || HavePotFitGroups(rot) )
+                er->out_angles  = open_angles_out(opt2fn("-ra",nfile,fnm), rot, oenv);
+            if ( HaveFlexibleGroups(rot) )
+                er->out_torque  = open_torque_out(opt2fn("-rt",nfile,fnm), rot, oenv);
+        }
+
+        sfree(x_pbc);
+    }
+}
+
+
+extern void finish_rot(FILE *fplog,t_rot *rot)
+{
+    gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
+
+    
+    er=rot->enfrot;
+    if (er->out_rot)
+        gmx_fio_fclose(er->out_rot);
+    if (er->out_slabs)
+        gmx_fio_fclose(er->out_slabs);
+    if (er->out_angles)
+        gmx_fio_fclose(er->out_angles);
+    if (er->out_torque)
+        gmx_fio_fclose(er->out_torque);
+}
+
+
+/* Rotate the local reference positions and store them in
+ * erg->xr_loc[0...(nat_loc-1)]
+ *
+ * Note that we already subtracted u or y_c from the reference positions
+ * in init_rot_group().
+ */
+static void rotate_local_reference(t_rotgrp *rotg)
+{
+    gmx_enfrotgrp_t erg;
+    int i,ii;
+
+    
+    erg=rotg->enfrotgrp;
+    
+    for (i=0; i<erg->nat_loc; i++)
+    {
+        /* Index of this rotation group atom with respect to the whole rotation group */
+        ii = erg->xc_ref_ind[i];
+        /* Rotate */
+        mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
+    }
+}
+
+
+/* Select the PBC representation for each local x position and store that
+ * for later usage. We assume the right PBC image of an x is the one nearest to
+ * its rotated reference */
+static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
+{
+    int d,i,ii,m;
+    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
+    rvec xref,xcurr,dx;
+    ivec shift;
+
+
+    erg=rotg->enfrotgrp;
+    
+    for (i=0; i<erg->nat_loc; i++)
+    {
+        clear_ivec(shift);
+        
+        /* Index of a rotation group atom  */
+        ii = erg->ind_loc[i];
+
+        /* Get the reference position. The pivot was already
+         * subtracted in init_rot_group() from the reference positions. Also,
+         * the reference positions have already been rotated in
+         * rotate_local_reference() */
+        copy_rvec(erg->xr_loc[i], xref);
+        
+        /* Subtract the (old) center from the current positions
+         * (just to determine the shifts!) */
+        rvec_sub(x[ii], erg->xc_center, xcurr);
+        
+        /* Shortest PBC distance between the atom and its reference */
+        rvec_sub(xcurr, xref, dx);
+        
+        /* Determine the shift for this atom */
+        for(m=npbcdim-1; m>=0; m--)
+        {
+            while (dx[m] < -0.5*box[m][m])
+            {
+                for(d=0; d<DIM; d++)
+                    dx[d] += box[m][d];
+                shift[m]++;
+            }
+            while (dx[m] >= 0.5*box[m][m])
+            {
+                for(d=0; d<DIM; d++)
+                    dx[d] -= box[m][d];
+                shift[m]--;
+            }
+        }
+        
+        /* Apply the shift to the current atom */
+        copy_rvec(x[ii], erg->x_loc_pbc[i]);
+        shift_single_coord(box, erg->x_loc_pbc[i], shift); 
+    }
+}
+
+
+extern void do_rotation(
+        t_commrec *cr,
+        t_inputrec *ir,
+        matrix box,
+        rvec x[],
+        real t,
+        gmx_large_int_t step,
+        gmx_wallcycle_t wcycle,
+        gmx_bool bNS)
+{
+    int      g,i,ii;
+    t_rot    *rot;
+    t_rotgrp *rotg;
+    gmx_bool outstep_slab, outstep_rot;
+    gmx_bool bFlex,bColl;
+    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
+    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
+    rvec     transvec;
+    t_gmx_potfit *fit=NULL;     /* For fit type 'potential' determine the fit
+                                   angle via the potential minimum            */
+
+    /* Enforced rotation cycle counting: */
+    gmx_cycles_t cycles_comp;   /* Cycles for the enf. rotation computation
+                                   only, does not count communication. This
+                                   counter is used for load-balancing         */
+
+#ifdef TAKETIME
+    double t0;
+#endif
+    
+    rot=ir->rot;
+    er=rot->enfrot;
+    
+    /* When to output in main rotation output file */
+    outstep_rot  = do_per_step(step, rot->nstrout) && er->bOut;
+    /* When to output per-slab data */
+    outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
+
+    /* Output time into rotation output file */
+    if (outstep_rot && MASTER(cr))
+        fprintf(er->out_rot, "%12.3e",t);
+
+    /**************************************************************************/
+    /* First do ALL the communication! */
+    for(g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        erg=rotg->enfrotgrp;
+
+        /* Do we have a flexible axis? */
+        bFlex = ISFLEX(rotg);
+        /* Do we use a collective (global) set of coordinates? */
+        bColl = ISCOLL(rotg);
+
+        /* Calculate the rotation matrix for this angle: */
+        erg->degangle = rotg->rate * t;
+        calc_rotmat(rotg->vec,erg->degangle,erg->rotmat);
+
+        if (bColl)
+        {
+            /* Transfer the rotation group's positions such that every node has
+             * all of them. Every node contributes its local positions x and stores
+             * it in the collective erg->xc array. */
+            communicate_group_positions(cr,erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
+                    x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
+        }
+        else
+        {
+            /* Fill the local masses array;
+             * this array changes in DD/neighborsearching steps */
+            if (bNS)
+            {
+                for (i=0; i<erg->nat_loc; i++)
+                {
+                    /* Index of local atom w.r.t. the collective rotation group */
+                    ii = erg->xc_ref_ind[i];
+                    erg->m_loc[i] = erg->mc[ii];
+                }
+            }
+
+            /* Calculate Omega*(y_i-y_c) for the local positions */
+            rotate_local_reference(rotg);
+
+            /* Choose the nearest PBC images of the group atoms with respect
+             * to the rotated reference positions */
+            choose_pbc_image(x, rotg, box, 3);
+
+            /* Get the center of the rotation group */
+            if ( (rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) )
+                get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
+        }
+
+    } /* End of loop over rotation groups */
+
+    /**************************************************************************/
+    /* Done communicating, we can start to count cycles for the load balancing now ... */
+    cycles_comp = gmx_cycles_read();
+
+
+#ifdef TAKETIME
+    t0 = MPI_Wtime();
+#endif
+
+    for(g=0; g<rot->ngrp; g++)
+    {
+        rotg = &rot->grp[g];
+        erg=rotg->enfrotgrp;
+
+        bFlex = ISFLEX(rotg);
+        bColl = ISCOLL(rotg);
+
+        if (outstep_rot && MASTER(cr))
+            fprintf(er->out_rot, "%12.4f", erg->degangle);
+
+        /* Calculate angles and rotation matrices for potential fitting: */
+        if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
+        {
+            fit = erg->PotAngleFit;
+            for (i = 0; i < rotg->PotAngle_nstep; i++)
+            {
+                calc_rotmat(rotg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
+
+                /* Clear value from last step */
+                erg->PotAngleFit->V[i] = 0.0;
+            }
+        }
+
+        /* Clear values from last time step */
+        erg->V        = 0.0;
+        erg->torque_v = 0.0;
+        erg->angle_v  = 0.0;
+        erg->weight_v = 0.0;
+
+        switch(rotg->eType)
+        {
+            case erotgISO:
+            case erotgISOPF:
+            case erotgPM:
+            case erotgPMPF:
+                do_fixed(rotg,x,box,t,step,outstep_rot,outstep_slab);
+                break;
+            case erotgRM:
+                do_radial_motion(rotg,x,box,t,step,outstep_rot,outstep_slab);
+                break;
+            case erotgRMPF:
+                do_radial_motion_pf(rotg,x,box,t,step,outstep_rot,outstep_slab);
+                break;
+            case erotgRM2:
+            case erotgRM2PF:
+                do_radial_motion2(rotg,x,box,t,step,outstep_rot,outstep_slab);
+                break;
+            case erotgFLEXT:
+            case erotgFLEX2T:
+                /* Subtract the center of the rotation group from the collective positions array
+                 * Also store the center in erg->xc_center since it needs to be subtracted
+                 * in the low level routines from the local coordinates as well */
+                get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+                svmul(-1.0, erg->xc_center, transvec);
+                translate_x(erg->xc, rotg->nat, transvec);
+                do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
+                break;
+            case erotgFLEX:
+            case erotgFLEX2:
+                /* Do NOT subtract the center of mass in the low level routines! */
+                clear_rvec(erg->xc_center);
+                do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
+                break;
+            default:
+                gmx_fatal(FARGS, "No such rotation potential.");
+                break;
+        }
+    }
+
+#ifdef TAKETIME
+    if (MASTER(cr))
+        fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
+#endif
+
+    /* Stop the enforced rotation cycle counter and add the computation-only
+     * cycles to the force cycles for load balancing */
+    cycles_comp  = gmx_cycles_read() - cycles_comp;
+
+    if (DOMAINDECOMP(cr) && wcycle)
+        dd_cycles_add(cr->dd,cycles_comp,ddCyclF);
+}
index 60ef80e6c348d3cfb45344f3b33a2967d3fdda04..d6809edca3ecc67bd530a93188600d6a1f35bc66 100644 (file)
@@ -80,7 +80,7 @@
 #include "trnio.h"
 #include "xtcio.h"
 #include "copyrite.h"
 #include "trnio.h"
 #include "xtcio.h"
 #include "copyrite.h"
-
+#include "pull_rotation.h"
 #include "mpelogging.h"
 #include "domdec.h"
 #include "partdec.h"
 #include "mpelogging.h"
 #include "domdec.h"
 #include "partdec.h"
@@ -630,14 +630,24 @@ void do_force(FILE *fplog,t_commrec *cr,
             dd_force_flop_start(cr->dd,nrnb);
         }
     }
             dd_force_flop_start(cr->dd,nrnb);
         }
     }
-       
+    
+    if (inputrec->bRot)
+    {
+        /* Enforced rotation has its own cycle counter that starts after the collective
+         * coordinates have been communicated. It is added to ddCyclF to allow
+         * for proper load-balancing */
+        wallcycle_start(wcycle,ewcROT);
+        do_rotation(cr,inputrec,box,x,t,step,wcycle,bNS);
+        wallcycle_stop(wcycle,ewcROT);
+    }
+
     /* Start the force cycle counter.
      * This counter is stopped in do_forcelow_level.
      * No parallel communication should occur while this counter is running,
      * since that will interfere with the dynamic load balancing.
      */
     wallcycle_start(wcycle,ewcFORCE);
     /* Start the force cycle counter.
      * This counter is stopped in do_forcelow_level.
      * No parallel communication should occur while this counter is running,
      * since that will interfere with the dynamic load balancing.
      */
     wallcycle_start(wcycle,ewcFORCE);
-    
+
     if (bDoForces)
     {
         /* Reset forces for which the virial is calculated separately:
     if (bDoForces)
     {
         /* Reset forces for which the virial is calculated separately:
@@ -822,6 +832,7 @@ void do_force(FILE *fplog,t_commrec *cr,
         }
     }
 
         }
     }
 
+    enerd->term[F_COM_PULL] = 0;
     if (inputrec->ePull == epullUMBRELLA || inputrec->ePull == epullCONST_F)
     {
         /* Calculate the center of mass forces, this requires communication,
     if (inputrec->ePull == epullUMBRELLA || inputrec->ePull == epullCONST_F)
     {
         /* Calculate the center of mass forces, this requires communication,
@@ -831,7 +842,7 @@ void do_force(FILE *fplog,t_commrec *cr,
          */
         set_pbc(&pbc,inputrec->ePBC,box);
         dvdl = 0; 
          */
         set_pbc(&pbc,inputrec->ePBC,box);
         dvdl = 0; 
-        enerd->term[F_COM_PULL] =
+        enerd->term[F_COM_PULL] +=
             pull_potential(inputrec->ePull,inputrec->pull,mdatoms,&pbc,
                            cr,t,lambda,x,f,vir_force,&dvdl);
         if (bSepDVDL)
             pull_potential(inputrec->ePull,inputrec->pull,mdatoms,&pbc,
                            cr,t,lambda,x,f,vir_force,&dvdl);
         if (bSepDVDL)
@@ -840,6 +851,14 @@ void do_force(FILE *fplog,t_commrec *cr,
         }
         enerd->dvdl_lin += dvdl;
     }
         }
         enerd->dvdl_lin += dvdl;
     }
+    
+    /* Add the forces from enforced rotation potentials (if any) */
+    if (inputrec->bRot)
+    {
+        wallcycle_start(wcycle,ewcROTadd);
+        enerd->term[F_COM_PULL] += add_rot_forces(inputrec->rot, f, cr,step,t);
+        wallcycle_stop(wcycle,ewcROTadd);
+    }
 
     if (PAR(cr) && !(cr->duty & DUTY_PME))
     {
 
     if (PAR(cr) && !(cr->duty & DUTY_PME))
     {
index 5e00f8351da548a9d643f934c131d0540f3970cd..fe7528170b05221176a0725c3416600bfb62c9d6 100644 (file)
@@ -1153,7 +1153,11 @@ static void make_benchmark_tprs(
  * not on mdrun command line options! */
 static gmx_bool tpr_triggers_file(const char *opt)
 {
  * not on mdrun command line options! */
 static gmx_bool tpr_triggers_file(const char *opt)
 {
-    if ( (0 == strcmp(opt, "-pf"))
+    if ( (0 == strcmp(opt, "-ro"))
+      || (0 == strcmp(opt, "-ra"))
+      || (0 == strcmp(opt, "-rt"))
+      || (0 == strcmp(opt, "-rs"))
+      || (0 == strcmp(opt, "-pf"))
       || (0 == strcmp(opt, "-px")) )
         return TRUE;
     else
       || (0 == strcmp(opt, "-px")) )
         return TRUE;
     else
@@ -2108,6 +2112,10 @@ int gmx_tune_pme(int argc,char *argv[])
       { efXVG, "-runav",  "runaver",  ffOPTWR },
       { efXVG, "-px",     "pullx",    ffOPTWR },
       { efXVG, "-pf",     "pullf",    ffOPTWR },
       { efXVG, "-runav",  "runaver",  ffOPTWR },
       { efXVG, "-px",     "pullx",    ffOPTWR },
       { efXVG, "-pf",     "pullf",    ffOPTWR },
+      { efXVG, "-ro",     "rotation", ffOPTWR },
+      { efLOG, "-ra",     "rotangles",ffOPTWR },
+      { efLOG, "-rs",     "rotslabs", ffOPTWR },
+      { efLOG, "-rt",     "rottorque",ffOPTWR },
       { efMTX, "-mtx",    "nm",       ffOPTWR },
       { efNDX, "-dn",     "dipole",   ffOPTWR },
       /* Output files that are deleted after each benchmark run */
       { efMTX, "-mtx",    "nm",       ffOPTWR },
       { efNDX, "-dn",     "dipole",   ffOPTWR },
       /* Output files that are deleted after each benchmark run */
@@ -2128,6 +2136,10 @@ int gmx_tune_pme(int argc,char *argv[])
       { efXVG, "-brunav", "benchrnav",ffOPTWR },
       { efXVG, "-bpx",    "benchpx",  ffOPTWR },
       { efXVG, "-bpf",    "benchpf",  ffOPTWR },
       { efXVG, "-brunav", "benchrnav",ffOPTWR },
       { efXVG, "-bpx",    "benchpx",  ffOPTWR },
       { efXVG, "-bpf",    "benchpf",  ffOPTWR },
+      { efXVG, "-bro",    "benchrot", ffOPTWR },
+      { efLOG, "-bra",    "benchrota",ffOPTWR },
+      { efLOG, "-brs",    "benchrots",ffOPTWR },
+      { efLOG, "-brt",    "benchrott",ffOPTWR },
       { efMTX, "-bmtx",   "benchn",   ffOPTWR },
       { efNDX, "-bdn",    "bench",    ffOPTWR }
     };
       { efMTX, "-bmtx",   "benchn",   ffOPTWR },
       { efNDX, "-bdn",    "bench",    ffOPTWR }
     };