Merge branch 'release-4-5-patches' into rotation-4-5
authorCarsten Kutzner <ckutzne@ckutzne.mpibpc.intern>
Fri, 27 Aug 2010 09:34:59 +0000 (11:34 +0200)
committerCarsten Kutzner <ckutzne@ckutzne.mpibpc.intern>
Fri, 27 Aug 2010 09:34:59 +0000 (11:34 +0200)
Conflicts:
src/gmxlib/tpxio.c
src/mdlib/mdebin.c

25 files changed:
1  2 
include/gmx_wallcycle.h
include/names.h
include/pull_rotation.h
include/types/enums.h
include/types/inputrec.h
src/gmxlib/mvdata.c
src/gmxlib/names.c
src/gmxlib/network.c
src/gmxlib/tpxio.c
src/gmxlib/txtdump.c
src/kernel/CMakeLists.txt
src/kernel/Makefile.am
src/kernel/grompp.c
src/kernel/mdrun.c
src/kernel/readir.c
src/kernel/readir.h
src/kernel/readrot.c
src/kernel/runner.c
src/mdlib/Makefile.am
src/mdlib/domdec.c
src/mdlib/gmx_wallcycle.c
src/mdlib/mdebin.c
src/mdlib/pull_rotation.c
src/mdlib/sim_util.c
src/tools/gmx_tune_pme.c

index 896a8ff776fc207979f431b1dc7eb5b5c8fde1fa,9d5c5cf23bddedbdb7fffd7697de98bd3b490d4d..b00cd650e78bef477b024778a36aa10de9e76148
  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, ewcTEST, ewcNR };
  
extern bool wallcycle_have_counter(void);
gmx_bool wallcycle_have_counter(void);
  /* Returns if cycle counting is supported */
  
extern gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr);
+ gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr);
  /* Returns the wall cycle structure.
   * Returns NULL when cycle counting is not supported.
   */
diff --cc include/names.h
Simple merge
index abada651bff329e7f8f6d08a0d0ca7f6c6ce5ad1,0000000000000000000000000000000000000000..da635199ba928303257bacc8932665ced34bca6f
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,137 @@@
-         int step,gmx_wallcycle_t wcycle,bool bNS);
 +/*
 + * 
 + *                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, gmx_mtop_t *mtop, const output_env_t oenv, 
 +        unsigned long Flags);
 +
 +extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot,t_mdatoms *md);
 +
 +/*! \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,
++        int 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, int 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
Simple merge
index 49e0e1aa0788ad50d0d58c89a0fe26a9c4782660,7c993638fc930bdde4e56413cdfc3e86db57563c..7715b212bb9cbe8bae2e7beb636c75abcd7b3537
@@@ -300,8 -267,6 +300,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          */
-   bool bRot;            /* Calculate enforced rotation potential(s)?    */
++  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                   */
index afc3b6afcdf7199b50d7e2c771e695b85f1a101f,6720f5b22d227680a2899db5e9c47001e4f0590e..d422f2546300c2717e4289d4e247cd0d7cf0249c
@@@ -458,30 -458,9 +458,30 @@@ static void bc_pull(const t_commrec *cr
    }
  }
  
 +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)
  {
-   bool bAlloc=TRUE;
+   gmx_bool bAlloc=TRUE;
    int i;
    
    block_bc(cr,*inputrec);
Simple merge
Simple merge
index 15061a80e1e079a45b071ad03c0877d04f58bc8e,8e4f3766f029722ed8b7edb0042ccdd12390baf6..19af818aa25141b281ee28e1ffaf0b594c74660d
@@@ -247,46 -249,7 +249,46 @@@ static void do_pull(t_fileio *fio, t_pu
      do_pullgrp(fio,&pull->grp[g],bRead,file_version);
  }
  
- static void do_rotgrp(t_fileio *fio, t_rotgrp *rotg,bool bRead, int file_version)
 +
-   bool bDum=TRUE;
++static void do_rotgrp(t_fileio *fio, t_rotgrp *rotg,gmx_bool bRead, int file_version)
 +{
- static void do_rot(t_fileio *fio, t_rot *rot,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);
 +}
 +
- static void do_inputrec(t_fileio *fio, t_inputrec *ir,bool bRead, 
++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->nsttout);
 +  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)
  {
      int  i,j,k,*tmp,idum=0; 
index 6ca35fccb182ab0d961e83427a3d8df808d8c98f,1314861cacebe010595bf232c374fdccd39e2237..4117af0654be773d0cc7ac726b769bd8bb46aaab
@@@ -508,38 -508,8 +508,38 @@@ static void pr_pull(FILE *fp,int indent
      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));
 +}
 +
 +static void pr_rot(FILE *fp,int indent,t_rot *rot)
 +{
 +  int g;
 +
 +  PI("rot_nstrout",rot->nstrout);
 +  PI("rot_nsttout",rot->nsttout);
 +  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,
-                  bool bMDPformat)
+                  gmx_bool bMDPformat)
  {
    const char *infbuf="inf";
    int  i;
Simple merge
index 27ff1a26aba54c746a219f9d388af47fd29cfa09,06bff7a2a68991d53c5f91ef9c6f3b881e40c3ab..2110d1fda5e58d14881fdec8af97efd84705f507
@@@ -37,10 -31,10 +31,10 @@@ gpp_bond_atomtype.c        gpp_bond_atomtype.
  h_db.c                h_db.h          \
  hackblock.c           \
  hizzie.c      hizzie.h        \
- pdb2top.h     pdb2top.c       \
+ pdb2top.c     \
  pgutil.c      pgutil.h        \
  readir.c      readir.h        \
 -readpull.c    \
 +readpull.c    readrot.c       \
  resall.c      \
  sorting.c     sorting.h       \
  specbond.c    specbond.h      \
Simple merge
Simple merge
Simple merge
Simple merge
index 8a9c0bb1c5cfcb3d17b66bbbc1b058f262dbb33d,0000000000000000000000000000000000000000..b4ef67a398bc35a23c5d4ac647d1f3e09460203d
mode 100644,000000..100644
--- /dev/null
@@@ -1,276 -1,0 +1,276 @@@
-     bool bSame=TRUE;
 +/*
 + *                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 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 */
 +    ITYPE("rot_nstrout",     rot->nstrout, 100);
 +    ITYPE("rot_nsttout",     rot->nsttout, 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 or flex2");
 +        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, "Enforced rotation: Group %d (%s) normalized rot. vector: %f %f %f\n", 
 +                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 iso, pm, rm, rm2 potential [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 rotation [nm] (flexible axis only)");
 +        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 for the force to be evaluated (for flex and flex2 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 actual angle of rotation group (rmsd or norm) (flex and flex2 pot.)");
 +        sprintf(buf,"rot_fit_method%d",g);
 +        ETYPE(buf, rotg->eFittype, erotg_fitnames);
 +    }
 +    
 +    *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, "Enforced rotation: Box size in reference file %s differs from actual box size!", fn);
 +        warning_note(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,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, "Enforced rotation: group %d has %d reference positions.",g,rotg->nat);
 +         snew(rotg->x_ref, rotg->nat);
 +         
 +         sprintf(reffile, "%s.%d.%s", base,g,extension);
 +         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]);
 +    }
 +}
Simple merge
Simple merge
Simple merge
index 80c723de811f4c6b26c9e716450458246b0cfed9,5dbbc23bc6d18ee77c1224ea64d2888d1cdd1785..851153811bbd80b533e87ca86612c179eab3af43
@@@ -76,9 -76,9 +76,9 @@@ typedef struct gmx_wallcycl
  
  /* 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", "Test" };
  
- bool wallcycle_have_counter(void)
gmx_bool wallcycle_have_counter(void)
  {
    return gmx_cycles_have_counter();
  }
index 682c424007341b07d19f2cee3ea3485215ae59d8,91e4225d5b68c48ca3e6ecdd294408961cfa1009..e05ccf06c41db7e5d33ae8fb6b86d9ce48f9bc08
@@@ -78,154 -82,174 +82,174 @@@ static const char *boxvel_nm[] = 
  #define NBOXS asize(boxs_nm)
  #define NTRICLBOXS asize(tricl_boxs_nm)
  
- static bool bTricl,bDynBox;
+ static gmx_bool bTricl,bDynBox;
  static int  f_nre=0,epc,etc,nCrmsd;
  
  t_mdebin *init_mdebin(ener_file_t fp_ene,
                        const gmx_mtop_t *mtop,
-                       const t_inputrec *ir)
+                       const t_inputrec *ir,
+                       FILE *fp_dhdl)
  {
-   const char *ener_nm[F_NRE];
-   static const char *vir_nm[] = {
-     "Vir-XX", "Vir-XY", "Vir-XZ",
-     "Vir-YX", "Vir-YY", "Vir-YZ",
-     "Vir-ZX", "Vir-ZY", "Vir-ZZ"
-   };
-   static const char *sv_nm[] = {
-     "ShakeVir-XX", "ShakeVir-XY", "ShakeVir-XZ",
-     "ShakeVir-YX", "ShakeVir-YY", "ShakeVir-YZ",
-     "ShakeVir-ZX", "ShakeVir-ZY", "ShakeVir-ZZ"
-   };
-   static const char *fv_nm[] = {
-     "ForceVir-XX", "ForceVir-XY", "ForceVir-XZ",
-     "ForceVir-YX", "ForceVir-YY", "ForceVir-YZ",
-     "ForceVir-ZX", "ForceVir-ZY", "ForceVir-ZZ"
-   };
-   static const char *pres_nm[] = {
-     "Pres-XX","Pres-XY","Pres-XZ",
-     "Pres-YX","Pres-YY","Pres-YZ",
-     "Pres-ZX","Pres-ZY","Pres-ZZ"
-   };
-   static const char *surft_nm[] = {
-     "#Surf*SurfTen"
-   };
-   static const char *mu_nm[] = {
-     "Mu-X", "Mu-Y", "Mu-Z"
-   };
-   static const char *vcos_nm[] = {
-     "2CosZ*Vel-X"
-   };
-   static const char *visc_nm[] = {
-     "1/Viscosity"
-   };
-   static const char *baro_nm[] = {
-     "Barostat"
-   };
-   char     **grpnms;
-   const gmx_groups_t *groups;
-   char     **gnm;
-   char     buf[256];
-   const char     *bufi;
-   t_mdebin *md;
-   int      i,j,ni,nj,n,nh,k,kk,ncon,nset;
-   bool     bBHAM,bNoseHoover,b14;
-   snew(md,1);
-   groups = &mtop->groups;
-   bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
-   b14   = (gmx_mtop_ftype_count(mtop,F_LJ14) > 0 ||
-          gmx_mtop_ftype_count(mtop,F_LJC14_Q) > 0);
-   ncon = gmx_mtop_ftype_count(mtop,F_CONSTR);
-   nset = gmx_mtop_ftype_count(mtop,F_SETTLE);
-   md->bConstr    = (ncon > 0 || nset > 0);
-   md->bConstrVir = FALSE;
-   if (md->bConstr) {
-     if (ncon > 0 && ir->eConstrAlg == econtLINCS) {
-       if (ir->eI == eiSD2)
-       md->nCrmsd = 2;
-       else
-       md->nCrmsd = 1;
+     const char *ener_nm[F_NRE];
+     static const char *vir_nm[] = {
+         "Vir-XX", "Vir-XY", "Vir-XZ",
+         "Vir-YX", "Vir-YY", "Vir-YZ",
+         "Vir-ZX", "Vir-ZY", "Vir-ZZ"
+     };
+     static const char *sv_nm[] = {
+         "ShakeVir-XX", "ShakeVir-XY", "ShakeVir-XZ",
+         "ShakeVir-YX", "ShakeVir-YY", "ShakeVir-YZ",
+         "ShakeVir-ZX", "ShakeVir-ZY", "ShakeVir-ZZ"
+     };
+     static const char *fv_nm[] = {
+         "ForceVir-XX", "ForceVir-XY", "ForceVir-XZ",
+         "ForceVir-YX", "ForceVir-YY", "ForceVir-YZ",
+         "ForceVir-ZX", "ForceVir-ZY", "ForceVir-ZZ"
+     };
+     static const char *pres_nm[] = {
+         "Pres-XX","Pres-XY","Pres-XZ",
+         "Pres-YX","Pres-YY","Pres-YZ",
+         "Pres-ZX","Pres-ZY","Pres-ZZ"
+     };
+     static const char *surft_nm[] = {
+         "#Surf*SurfTen"
+     };
+     static const char *mu_nm[] = {
+         "Mu-X", "Mu-Y", "Mu-Z"
+     };
+     static const char *vcos_nm[] = {
+         "2CosZ*Vel-X"
+     };
+     static const char *visc_nm[] = {
+         "1/Viscosity"
+     };
+     static const char *baro_nm[] = {
+         "Barostat"
+     };
+     char     **grpnms;
+     const gmx_groups_t *groups;
+     char     **gnm;
+     char     buf[256];
+     const char     *bufi;
+     t_mdebin *md;
+     int      i,j,ni,nj,n,nh,k,kk,ncon,nset;
+     gmx_bool     bBHAM,bNoseHoover,b14;
+     snew(md,1);
+     if (EI_DYNAMICS(ir->eI))
+     {
+         md->delta_t = ir->delta_t;
+     }
+     else
+     {
+         md->delta_t = 0;
+     }
+     groups = &mtop->groups;
+     bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
+     b14   = (gmx_mtop_ftype_count(mtop,F_LJ14) > 0 ||
+              gmx_mtop_ftype_count(mtop,F_LJC14_Q) > 0);
+     ncon = gmx_mtop_ftype_count(mtop,F_CONSTR);
+     nset = gmx_mtop_ftype_count(mtop,F_SETTLE);
+     md->bConstr    = (ncon > 0 || nset > 0);
+     md->bConstrVir = FALSE;
+     if (md->bConstr) {
+         if (ncon > 0 && ir->eConstrAlg == econtLINCS) {
+             if (ir->eI == eiSD2)
+                 md->nCrmsd = 2;
+             else
+                 md->nCrmsd = 1;
+         }
+         md->bConstrVir = (getenv("GMX_CONSTRAINTVIR") != NULL);
+     } else {
+         md->nCrmsd = 0;
+     }
+     /* Energy monitoring */
+     for(i=0;i<egNR;i++)
+     {
+         md->bEInd[i]=FALSE;
+     }
+     for(i=0; i<F_NRE; i++) {
+         md->bEner[i] = FALSE;
+         if (i == F_LJ)
+             md->bEner[i] = !bBHAM;
+         else if (i == F_BHAM)
+             md->bEner[i] = bBHAM;
+         else if (i == F_EQM)
+             md->bEner[i] = ir->bQMMM;
+         else if (i == F_COUL_LR)
+             md->bEner[i] = (ir->rcoulomb > ir->rlist);
+         else if (i == F_LJ_LR)
+             md->bEner[i] = (!bBHAM && ir->rvdw > ir->rlist);
+         else if (i == F_BHAM_LR)
+             md->bEner[i] = (bBHAM && ir->rvdw > ir->rlist);
+         else if (i == F_RF_EXCL)
+             md->bEner[i] = (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC);
+         else if (i == F_COUL_RECIP)
+             md->bEner[i] = EEL_FULL(ir->coulombtype);
+         else if (i == F_LJ14)
+             md->bEner[i] = b14;
+         else if (i == F_COUL14)
+             md->bEner[i] = b14;
+         else if (i == F_LJC14_Q || i == F_LJC_PAIRS_NB)
+             md->bEner[i] = FALSE;
+         else if ((i == F_DVDL) || (i == F_DKDL))
+             md->bEner[i] = (ir->efep != efepNO);
+         else if (i == F_DHDL_CON)
+             md->bEner[i] = (ir->efep != efepNO && md->bConstr);
+         else if ((interaction_function[i].flags & IF_VSITE) ||
+                  (i == F_CONSTR) || (i == F_CONSTRNC) || (i == F_SETTLE))
+             md->bEner[i] = FALSE;
+         else if ((i == F_COUL_SR) || (i == F_EPOT) || (i == F_PRES)  || (i==F_EQM))
+             md->bEner[i] = TRUE;
+         else if ((i == F_GBPOL) && ir->implicit_solvent==eisGBSA)
+             md->bEner[i] = TRUE;
+         else if ((i == F_NPSOLVATION) && ir->implicit_solvent==eisGBSA && (ir->sa_algorithm != esaNO))
+             md->bEner[i] = TRUE;
+         else if ((i == F_GB12) || (i == F_GB13) || (i == F_GB14))
+             md->bEner[i] = FALSE;
+         else if ((i == F_ETOT) || (i == F_EKIN) || (i == F_TEMP))
+             md->bEner[i] = EI_DYNAMICS(ir->eI);
+         else if (i==F_VTEMP) 
+             md->bEner[i] =  (EI_DYNAMICS(ir->eI) && getenv("GMX_VIRIAL_TEMPERATURE"));
+         else if (i == F_DISPCORR || i == F_PDISPCORR)
+             md->bEner[i] = (ir->eDispCorr != edispcNO);
+         else if (i == F_DISRESVIOL)
+             md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_DISRES) > 0);
+         else if (i == F_ORIRESDEV)
+             md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0);
+         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
+             md->bEner[i] = (gmx_mtop_ftype_count(mtop,i) > 0);
+     }
+     md->f_nre=0;
+     for(i=0; i<F_NRE; i++)
+     {
+         if (md->bEner[i])
+         {
+             /* FIXME: The constness should not be cast away */
+             /*ener_nm[f_nre]=(char *)interaction_function[i].longname;*/
+             ener_nm[md->f_nre]=interaction_function[i].longname;
+             md->f_nre++;
+         }
      }
-     md->bConstrVir = (getenv("GMX_CONSTRAINTVIR") != NULL);
-   } else {
-     md->nCrmsd = 0;
-   }
-   /* Energy monitoring */
-   for(i=0;i<egNR;i++)
-   {
-       md->bEInd[i]=FALSE;
-   }
-   for(i=0; i<F_NRE; i++) {
-       md->bEner[i] = FALSE;
-       if (i == F_LJ)
-           md->bEner[i] = !bBHAM;
-       else if (i == F_BHAM)
-           md->bEner[i] = bBHAM;
-       else if (i == F_EQM)
-           md->bEner[i] = ir->bQMMM;
-       else if (i == F_COUL_LR)
-           md->bEner[i] = (ir->rcoulomb > ir->rlist);
-       else if (i == F_LJ_LR)
-           md->bEner[i] = (!bBHAM && ir->rvdw > ir->rlist);
-       else if (i == F_BHAM_LR)
-           md->bEner[i] = (bBHAM && ir->rvdw > ir->rlist);
-       else if (i == F_RF_EXCL)
-           md->bEner[i] = (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC);
-       else if (i == F_COUL_RECIP)
-           md->bEner[i] = EEL_FULL(ir->coulombtype);
-       else if (i == F_LJ14)
-           md->bEner[i] = b14;
-       else if (i == F_COUL14)
-           md->bEner[i] = b14;
-       else if (i == F_LJC14_Q || i == F_LJC_PAIRS_NB)
-           md->bEner[i] = FALSE;
-       else if ((i == F_DVDL) || (i == F_DKDL))
-           md->bEner[i] = (ir->efep != efepNO);
-       else if (i == F_DHDL_CON)
-           md->bEner[i] = (ir->efep != efepNO && md->bConstr);
-       else if ((interaction_function[i].flags & IF_VSITE) ||
-                (i == F_CONSTR) || (i == F_CONSTRNC) || (i == F_SETTLE))
-           md->bEner[i] = FALSE;
-       else if ((i == F_COUL_SR) || (i == F_EPOT) || (i == F_PRES)  || (i==F_EQM))
-           md->bEner[i] = TRUE;
-       else if ((i == F_ETOT) || (i == F_EKIN) || (i == F_TEMP))
-           md->bEner[i] = EI_DYNAMICS(ir->eI);
-       else if (i==F_VTEMP) 
-           md->bEner[i] =  (EI_DYNAMICS(ir->eI) && getenv("GMX_VIRIAL_TEMPERATURE"));
-       else if (i == F_DISPCORR || i == F_PDISPCORR)
-           md->bEner[i] = (ir->eDispCorr != edispcNO);
-       else if (i == F_DISRESVIOL)
-           md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_DISRES) > 0);
-       else if (i == F_ORIRESDEV)
-           md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0);
-       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 || ir->bRot);
-       else if (i == F_ECONSERVED)
-           md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
-                           (ir->epc == epcNO || ir->epc==epcMTTK));
-       else
-           md->bEner[i] = (gmx_mtop_ftype_count(mtop,i) > 0);
-   }
-   
-   md->f_nre=0;
-   for(i=0; i<F_NRE; i++)
-   {
-       if (md->bEner[i])
-       {
-           /* FIXME: The constness should not be cast away */
-           /*ener_nm[f_nre]=(char *)interaction_function[i].longname;*/
-           ener_nm[md->f_nre]=interaction_function[i].longname;
-           md->f_nre++;
-       }
-   }
  
      md->epc = ir->epc;
      for (i=0;i<DIM;i++) 
index b230a5629aaeb2754fad5f4458ac965fa0c37116,0000000000000000000000000000000000000000..112917a3b8c17047b365fa2992eff604272bcf7c
mode 100644,000000..100644
--- /dev/null
@@@ -1,3339 -1,0 +1,3339 @@@
-     bool     bFlex;
 +/*
 + * 
 + *                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 "mpelogging.h"
 +#include "groupcoord.h"
 +
 +
 +/* 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;
 +
 +
 +/* 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                 */
 +    real  Vrot;             /* (Local) part of the enf. rotation potential    */
 +} t_gmx_enfrot;
 +
 +
 +/* Global enforced rotation data for a single rotation group                  */
 +typedef struct gmx_enfrotgrp
 +{
 +    real    degangle;       /* Rotation angle in degree                       */
 +    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  */
 +    /* Fixed rotation only */
 +    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              */
 +    real  fix_torque_v;     /* Torque in the direction of rotation vector     */
 +    real  fix_angles_v;
 +    real  fix_weight_v;
 +    /* 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 reference COG 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 (COG)            */
 +    rvec  *slab_center_ref; /* Gaussian-weighted slab COG 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                   */
 +} t_gmx_enfrotgrp;
 +
 +
 +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);
 +}
 +
 +
 +/* Output rotation energy and torque for each rotation group */
 +static void reduce_output(t_commrec *cr, t_rot *rot, real t)
 +{
 +    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           */
-         bool      bOutStep,   /* Is this an output step?                      */
-         bool      bReference) /* If this routine is called from
++    gmx_bool bFlex;
 +
 +    
 +    er=rot->enfrot;
 +    
 +    /* Fill the MPI buffer with stuff to reduce: */
 +    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;
 +            switch (rotg->eType)
 +            {
 +                case erotgISO:
 +                case erotgISOPF:
 +                case erotgPM:
 +                case erotgPMPF:
 +                case erotgRM:
 +                case erotgRMPF:
 +                case erotgRM2:
 +                case erotgRM2PF:
 +                    er->mpi_inbuf[count++] = erg->fix_torque_v;
 +                    er->mpi_inbuf[count++] = erg->fix_angles_v;
 +                    er->mpi_inbuf[count++] = erg->fix_weight_v;
 +                    break;
 +                case erotgFLEX:
 +                case erotgFLEX2:
 +                    /* (Re-)allocate memory for MPI buffer: */
 +                    if (er->mpi_bufsize < count+nslabs)
 +                    {
 +                        er->mpi_bufsize = count+nslabs;
 +                        srenew(er->mpi_inbuf , er->mpi_bufsize);
 +                        srenew(er->mpi_outbuf, er->mpi_bufsize);
 +                    }
 +                    for (i=0; i<nslabs; i++)
 +                        er->mpi_inbuf[count++] = erg->slab_torque_v[i];
 +                    break;
 +                default:
 +                    break;
 +            }
 +        }
 +#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++];
 +                switch (rotg->eType)
 +                {
 +                    case erotgISO:
 +                    case erotgISOPF:
 +                    case erotgPM:
 +                    case erotgPMPF:
 +                    case erotgRM:
 +                    case erotgRMPF:
 +                    case erotgRM2:
 +                    case erotgRM2PF:
 +                        erg->fix_torque_v = er->mpi_outbuf[count++];
 +                        erg->fix_angles_v = er->mpi_outbuf[count++];
 +                        erg->fix_weight_v = er->mpi_outbuf[count++];
 +                        break;
 +                    case erotgFLEX:
 +                    case erotgFLEX2:
 +                        for (i=0; i<nslabs; i++)
 +                            erg->slab_torque_v[i] = er->mpi_outbuf[count++];
 +                        break;
 +                    default:
 +                        break;
 +                }
 +            }
 +        }
 +    }
 +    
 +    /* Output */
 +    if (MASTER(cr))
 +    {
 +        /* Av. angle and total torque for each rotation group */
 +        for (g=0; g < rot->ngrp; g++)
 +        {
 +            rotg=&rot->grp[g];
 +            bFlex = (rotg->eType==erotgFLEX) || (rotg->eType==erotgFLEX2);
 +
 +            erg=rotg->enfrotgrp;
 +            
 +            /* Output to main rotation log file: */
 +            if (!bFlex)
 +            {
 +                fprintf(er->out_rot, "%12.4f%12.3e", 
 +                        (erg->fix_angles_v/erg->fix_weight_v)*180.0*M_1_PI,
 +                        erg->fix_torque_v);
 +            }
 +            fprintf(er->out_rot, "%12.3e", erg->V);
 +                        
 +            /* 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");
 +            }
 +        }
 +        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, int 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           */
 +
 +    
 +    er=rot->enfrot;
 +    
 +    GMX_MPE_LOG(ev_add_rot_forces_start);
 +    
 +    /* 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->nsttout))
 +        reduce_output(cr, rot, t);
 +
 +    /* Total rotation potential is the sum over all rotation groups */
 +    er->Vrot = 0.0; 
 +        
 +    /* 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;
 +        er->Vrot += erg->V;
 +        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]);
 +        }
 +    }
 +    
 +    GMX_MPE_LOG(ev_add_rot_forces_finish);
 +
 +    return (MASTER(cr)? er->Vrot : 0.0);
 +}
 +
 +
 +/* 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)
 +{
 +    const double norm = 0.5698457353514458216;  /* = 1/1.7548609 */
 +    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/norm;
 +    if (arg > 1.0)
 +        gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", norm);
 +    
 +    return sqrt(-2.0*sigma*sigma*log(min_gaussian/norm));
 +}
 +
 +
 +static inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
 +{
 +    return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
 +}
 +
 +
 +static inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
 +{
 +    /* norm is chosen such that the sum of the gaussians
 +     * over the slabs is approximately 1.0 everywhere */
 +    const real norm = 0.5698457353514458216;  /* = 1/1.7548609 */
 +    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       */
 +        t_commrec *cr,        /* Communication record                         */
 +        int       g,          /* The number of the rotation group             */
 +        real      time,       /* Used for output only                         */
 +        FILE      *out_slabs, /* For outputting center per slab information   */
-         bool      bCalcTorque,
++        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 (MASTER(cr) && 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 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);
 +}
 +
 +
 +/* Right-aligned output of value with standard width */
 +static void print_aligned_group(FILE *fp, char *str, int g)
 +{
 +    char sbuf[STRLEN];
 +    
 +    
 +    sprintf(sbuf, "%s%d", str, g);
 +    fprintf(fp, "%12s", sbuf);
 +}
 +
 +
 +static FILE *open_output_file(const char *fn, int steps)
 +{
 +    FILE *fp;
 +    
 +    
 +    fp = ffopen(fn, "w");
 +
 +    fprintf(fp, "# Output is written every %d time steps.\n\n", steps);
 +    
 +    return fp;
 +}
 +
 +
 +/* Open output file for slab COG data. Call on master only */
 +static FILE *open_slab_out(t_rot *rot, const char *fn)
 +{
 +    FILE      *fp=NULL;
 +    int       g;
 +    t_rotgrp  *rotg;
 +
 +
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEX2)
 +        {
 +            if (NULL == fp)
 +                fp = open_output_file(fn, rot->nsttout);
 +            fprintf(fp, "# Rotation group %d (%s), slab distance %f nm\n", g, erotg_names[rotg->eType], rotg->slab_dist);
 +        }
 +    }
 +    
 +    if (fp != NULL)
 +    {
 +        fprintf(fp, "# The following columns will have the syntax: (COG = center of geometry, gaussian weighted)\n");
 +        fprintf(fp, "#     ");
 +        print_aligned_short(fp, "t");
 +        print_aligned_short(fp, "grp");
 +        print_aligned_short(fp, "slab");
 +        print_aligned(fp, "COG-X");
 +        print_aligned(fp, "COG-Y");
 +        print_aligned(fp, "COG-Z");
 +        print_aligned_short(fp, "slab");
 +        print_aligned(fp, "COG-X");
 +        print_aligned(fp, "COG-Y");
 +        print_aligned(fp, "COG-Z");
 +        print_aligned_short(fp, "slab");
 +        fprintf(fp, " ...\n");
 +        fflush(fp);
 +    }
 +    
 +    return fp;
 +}
 +
 +
 +/* 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, 
 +                          unsigned long Flags)
 +{
 +    FILE       *fp;
 +    int        g,nsets;
 +    t_rotgrp   *rotg;
 +    const char **setname;
 +    char       buf[50];
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +
 +    
 +    if (Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn,"a");
 +    }
 +    else
 +    {
 +        fp = xvgropen(fn, "Rotation angles and energy", "Time [ps]", "angles [degree] and energies [kJ/mol]", oenv);
 +        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#\n");
 +        
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            erg=rotg->enfrotgrp;
 +
 +            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 degree/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 ( (rotg->eType==erotgFLEX) || (rotg->eType==erotgFLEX2) )
 +            {
 +                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==erotgFLEX) || (rotg->eType==erotgFLEX2)) )
 +            {
 +                fprintf(fp, "# %s of ref. grp. %d  %12.5e %12.5e %12.5e\n",
 +                        rotg->bMassW? "COM":"COG", g,
 +                                erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
 +
 +                fprintf(fp, "# initial %s grp. %d  %12.5e %12.5e %12.5e\n",
 +                        rotg->bMassW? "COM":"COG", g,
 +                                erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
 +            }
 +
 +            if ( (rotg->eType == erotgRM2) || (rotg->eType==erotgFLEX2) )
 +            {
 +                fprintf(fp, "# rot_eps%d            %12.5e nm^2\n", g, rotg->eps);
 +            }
 +        }
 +        
 +        fprintf(fp, "#\n# Legend for the following data columns:\n");
 +        fprintf(fp, "#     ");
 +        print_aligned_short(fp, "t");
 +        nsets = 0;
 +        snew(setname, 4*rot->ngrp);
 +        
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            sprintf(buf, "theta_ref%d [degree]", g);
 +            print_aligned_group(fp, "theta_ref", g);
 +            setname[nsets] = strdup(buf);
 +            nsets++;
 +        }
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            /* For flexible axis rotation we use RMSD fitting to determine the
 +             * actual angle of the rotation group */
 +            if ( (rotg->eType!=erotgFLEX) && (rotg->eType!=erotgFLEX2) )
 +            {
 +                sprintf(buf, "theta-av%d [degree]", g);
 +                print_aligned_group(fp, "theta_av", g);
 +                setname[nsets] = strdup(buf);
 +                nsets++;
 +                sprintf(buf, "tau%d [kJ/mol]", g);
 +                print_aligned_group(fp, "tau", g);
 +                setname[nsets] = strdup(buf);
 +                nsets++;
 +            }
 +            sprintf(buf, "energy%d [kJ/mol]", g);
 +            print_aligned_group(fp, "energy", g);
 +            setname[nsets] = strdup(buf);
 +            nsets++;
 +        }
 +        fprintf(fp, "\n#\n");
 +        
 +        if (nsets > 1)
 +            xvgr_legend(fp, nsets, setname, oenv);
 +        sfree(setname);
 +        
 +        fflush(fp);
 +    }
 +    
 +    return fp;
 +}
 +
 +
 +/* Call on master only */
 +static FILE *open_angles_out(t_rot *rot, const char *fn)
 +{
 +    int      g;
 +    FILE     *fp=NULL;
 +    t_rotgrp *rotg;
 +
 +
 +    /* Open output file and write some information about it's structure: */
 +    fp = open_output_file(fn, rot->nstrout);
 +    fprintf(fp, "# All angles given in degrees, time in ps\n");
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEX2)
 +            fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, fit type %s\n", 
 +                    g, erotg_names[rotg->eType], rotg->slab_dist, erotg_fitnames[rotg->eFittype]);
 +    }
 +    fprintf(fp, "# The following columns will have the syntax:\n");
 +    fprintf(fp, "#     ");
 +    print_aligned_short(fp, "t");
 +    print_aligned_short(fp, "grp");
 +    print_aligned(fp, "theta_ref");
 +    print_aligned(fp, "theta_fit");
 +    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, " ...\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(t_rot *rot, const char *fn)
 +{
 +    FILE      *fp;
 +    int       g;
 +    t_rotgrp  *rotg;
 +
 +
 +    fp = open_output_file(fn, rot->nsttout);
 +
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEX2)
 +        {
 +            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, "# The following columns will have the syntax (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 double 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 (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;
 +        
 +    /* 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 opt_angle;
 +}
 +
 +
 +/* Determine actual angle of this slab by RMSD fit */
 +/* Not parallelized, call this routine only on the master */
 +/* TODO: make this routine mass-weighted */
 +static void flex_fit_angle(
 +        int  g,
 +        t_rotgrp *rotg,
 +        double t,
 +        real degangle,
 +        FILE *fp)
 +{
 +    int         i,l,n,islab,ind;
 +    rvec        curr_x, ref_x;
 +    rvec        *fitcoords=NULL;
 +    rvec        act_center;  /* Center of actual positions that are passed to the fit routine */
 +    rvec        ref_center;  /* Same for the reference positions */
 +    double      fitangle;    /* This will be the actual angle of the rotation group derived
 +                              * from an RMSD fit to the reference structure at t=0 */
 +    t_gmx_slabdata *sd;
 +    rvec        coord;
 +    real        scal;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /**********************************/
 +    /* First collect the data we need */
 +    /**********************************/
 +
 +    /* Loop over 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_flex! */
 +            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]);
 +            /* Save the weight for this atom in this slab */
 +            sd->weight[ind] = gaussian_weight(curr_x, rotg, n);
 +            
 +            /* Next atom in this slab */
 +            ind++;
 +        }
 +    }
 +
 +    /* Get the geometrical center of the whole rotation group: */
 +    get_center(erg->xc, NULL, rotg->nat, act_center);
 +
 +
 +    /******************************/
 +    /* Now do the fit calculation */
 +    /******************************/
 +
 +    /* === Determine the optimal fit angle for the whole rotation group === */
 +    if (rotg->eFittype == erotgFitNORM)
 +    {
 +        /* Normalize every position to it's reference length 
 +         * prior to performing the fit */
 +        for (i=0; i<rotg->nat; i++)
 +        {
 +            /* First put geometrical center of positions into origin */
 +            rvec_sub(erg->xc[i], act_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 in buf[i] */
 +            svmul(scal, coord, erg->xc_norm[i]);
 +        }
 +        fitcoords = erg->xc_norm;
 +    }
 +    else
 +    {
 +        fitcoords = erg->xc;
 +    }
 +    /* Note that from the point of view of the current positions, the reference has rotated backwards,
 +     * but we want to output the angle relative to the fixed reference, therefore the minus sign. */
 +    fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, NULL, rotg->nat, erg->xc_ref_center, act_center, rotg->vec);    
 +    fprintf(fp, "%12.3e%6d%12.3f%12.3lf", t, g, degangle, fitangle);
 +
 +
 +    /* === 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 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 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_commrec *cr,
 +        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, t_commrec *cr)
 +{
 +    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, t_commrec *cr)
 +{
 +    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[],
-         bool      bCalcTorque,
++        gmx_bool  bCalcTorque,
 +        matrix    box,
 +        t_commrec *cr)
 +{
 +    int  count,ic,ii,j,m,n,islab,iigrp;
 +    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;
 +    rvec  rjn;               /* Helper variables                              */
 +    real  fac,fac2;
 +
 +    real OOpsij,OOpsijstar;
 +    real OOsigma2;           /* 1/(sigma^2)                                   */
 +    real sjn_rjn;
 +    real betasigpsi;
 +    rvec sjn,tmpvec,tmpvec2;
 +    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                            */
 +
 +    /* 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, cr);
 +
 +    /********************************************************/
 +    /* 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] */
 +        copy_rvec(x[ii], 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, cr, 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, tmpvec2);          /* tmpvec2 = yj0 - ycn      */
 +
 +            /* Rotate: */
 +            mvmul(erg->rotmat, tmpvec2, 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;
 +
 +
 +            /*************************************/
 +            /* 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 (bCalcTorque)
 +            {
 +                /* 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 INFOF
 +        fprintf(stderr," FORCE on ATOM %d/%d  = (%15.8f %15.8f %15.8f)  \n",
 +                j,ii,erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
 +#endif
 +
 +#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
 +    } /* END of loop over local atoms */
 +
 +#ifdef INFOF
 +    fprintf(stderr, "THE POTENTIAL IS  V=%f\n", V);
 +#endif
 +
 +    return V;
 +}
 +
 +
 +static real do_flex_lowlevel(
 +        t_rotgrp *rotg,
 +        real      sigma,     /* The Gaussian width sigma                      */
 +        rvec      x[],
-     static bool bFirst=1;
++        gmx_bool  bCalcTorque,
 +        matrix    box,
 +        t_commrec *cr)
 +{
 +    int   count,ic,ii,j,m,n,islab,iigrp;
 +    rvec  xj,yj0;            /* current and reference position                */
 +    rvec  xcn, ycn;          /* the current and the reference slab centers    */
 +    rvec  xj_xcn;            /* xj - xcn                                      */
 +    rvec  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  tmpvec,tmpvec2,tmp_f;   /* Helper variables            */
 +    real  V;                 /* The rotation potential energy                 */
 +    real  OOsigma2;          /* 1/(sigma^2)                                   */
 +    real  beta;              /* beta_n(xj)                                    */
 +    real  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       */
 +
 +    
 +    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, cr);
 +
 +    /********************************************************/
 +    /* 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] */
 +        copy_rvec(x[ii], 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, cr, 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, tmpvec); /* tmpvec = yj0 - ycn */
 +
 +            /* Rotate: */
 +            mvmul(erg->rotmat, tmpvec, 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.(xj-xcn) */
 +
 +                                 /*         v x Omega.(xj-xcn)    */
 +            unitv(tmpvec,qjn);   /*  qjn = --------------------   */
 +                                 /*        |v x Omega.(xj-xcn)|   */
 +
 +            bjn = iprod(qjn, xj_xcn);   /* bjn = qjn * (xj - xcn) */
 +            
 +            /*********************************/
 +            /* Add to the rotation potential */
 +            /*********************************/
 +            V += 0.5*rotg->k*wj*gaussian_xj*sqr(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);
 +            
 +            GMX_MPE_LOG(ev_inner_loop_finish);
 +
 +            /* Calculate the torque: */
 +            if (bCalcTorque)
 +            {
 +                /* The force on atom ii from slab n only: */
 +                rvec_sub(innersumvec, tmpvec2, force_n);
 +                svmul(rotg->k, force_n, 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];
 +#ifdef INFOF
 +        fprintf(stderr," FORCE on atom %d  = %15.8f %15.8f %15.8f   1: %15.8f %15.8f %15.8f   2: %15.8f %15.8f %15.8f\n", iigrp,
 +                rotg->k*tmp_f[XX] ,  rotg->k*tmp_f[YY] ,  rotg->k*tmp_f[ZZ] ,
 +               -rotg->k*sum_n1[XX], -rotg->k*sum_n1[YY], -rotg->k*sum_n1[ZZ],
 +                rotg->k*sum_n2[XX],  rotg->k*sum_n2[YY],  rotg->k*sum_n2[ZZ]);
 +#endif
 +    } /* END of loop over local atoms */
 +
 +    return V;
 +}
 +
 +#ifdef PRINT_COORDS
 +static void print_coordinates(t_commrec *cr, t_rotgrp *rotg, rvec x[], matrix box, int step)
 +{
 +    int i;
 +    static FILE *fp;
 +    static char buf[STRLEN];
-         bool      bOutstep)
++    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 */
 +    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, t_commrec *cr)
 +{
 +    int i,islab,n;
 +    real beta;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    GMX_MPE_LOG(ev_get_firstlast_start);
 +    
 +    /* 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);
 +    
 +     GMX_MPE_LOG(ev_get_firstlast_finish);
 +}
 +
 +
 +/* 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 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 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 */
 +        t_commrec       *cr)
 +{
 +    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, "Enforced rotation: No reference data for first slab (n=%d), unable to proceed.", 
 +                  erg->slab_first);
 +    
 +    /* Check whether we have reference data to compare against */
 +    if (erg->slab_last > erg->slab_last_ref)
 +        gmx_fatal(FARGS, "Enforced rotation: No reference data for last slab (n=%d), unable to proceed.",
 +                  erg->slab_last);
 +}
 +
 +
 +/* Enforced rotation with a flexible axis */
 +static void do_flexible(
 +        t_commrec *cr,
 +        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            */
 +        int       step,       /* The time step                  */
-         bool      bTorque)
++        gmx_bool  bOutstep)
 +{
 +    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, cr);
 +    
 +    /* 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, cr);
 +
 +    /* Determine the gaussian-weighted center of positions for all slabs */
 +    get_slab_centers(rotg,erg->xc,erg->mc_sorted,cr,g,t,enfrot->out_slabs,bOutstep,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 */
 +    GMX_MPE_LOG(ev_flexll_start);
 +    if (rotg->eType == erotgFLEX)
 +        erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstep, box, cr);
 +    else if (rotg->eType == erotgFLEX2)
 +        erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstep, box, cr);
 +    else
 +        gmx_fatal(FARGS, "Unknown flexible rotation type");
 +    GMX_MPE_LOG(ev_flexll_finish);
 +    
 +    /* Determine actual angle of this slab by RMSD fit and output to file - Let's hope */
 +    /* this only happens once in a while, since this is not parallelized! */
 +    if (bOutstep && MASTER(cr))
 +        flex_fit_angle(g, rotg, t, erg->degangle, enfrot->out_angles);
 +}
 +
 +
 +/* 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 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 an axis */
 +/* The atoms of the actual rotation group are attached with imaginary  */
 +/* springs to the reference atoms.                                     */
 +static void do_fixed(
 +        t_commrec *cr,
 +        t_rotgrp  *rotg,        /* The rotation group          */
 +        rvec      x[],          /* The positions               */
 +        matrix    box,          /* The simulation box          */
 +        double    t,            /* Time in picoseconds         */
 +        int       step,         /* The time step               */
-     bool      bProject;
++        gmx_bool  bTorque)
 +{
 +    int       i,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      tmpvec;
 +
 +    /* for mass weighting: */
 +    real      wi;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +    real      k_wi;            /* k times wi */
 +
-         bool      bTorque)
++    gmx_bool  bProject;
 +
 +    
 +    erg=rotg->enfrotgrp;
 +    bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
 +
 +    /* Clear values from last time step */
 +    erg->V            = 0.0;
 +    erg->fix_torque_v = 0.0;
 +    erg->fix_angles_v = 0.0;
 +    erg->fix_weight_v = 0.0;
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (i=0; i<erg->nat_loc; i++)
 +    {
 +        /* Calculate (x_i-x_c) resp. (x_i-u) */
 +        rvec_sub(erg->x_loc_pbc[i], erg->xc_center, tmpvec);
 +
 +        /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
 +        rvec_sub(erg->xr_loc[i], tmpvec, dr);
 +        
 +        if (bProject)
 +            project_onto_plane(dr, rotg->vec);
 +            
 +        /* Mass-weighting */
 +        wi = N_M*erg->m_loc[i];
 +
 +        /* 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[i][m] = tmp_f[m];
 +            erg->V              += 0.5*k_wi*sqr(dr[m]);
 +        }
 +        
 +        if (bTorque)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->fix_torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[i], erg->xc_center);
 +            
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, tmpvec, erg->xr_loc[i], &alpha, &weight);  /* angle in rad, weighted */
 +            erg->fix_angles_v += alpha * weight;
 +            erg->fix_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];
 +            }
 +         */
 +#ifdef INFOF
 +        fprintf(stderr,"step %d node%d FORCE on ATOM %d = (%15.8f %15.8f %15.8f)  torque=%15.8f\n", step, cr->nodeid,
 +                erg->xc_ref_ind[i],erg->f_rot_loc[i][XX], erg->f_rot_loc[i][YY], erg->f_rot_loc[i][ZZ],erg->fix_torque_v);
 +#endif
 +    } /* end of loop over local rotation group atoms */
 +}
 +
 +
 +/* Calculate the radial motion potential and forces */
 +static void do_radial_motion(
 +        t_commrec *cr,
 +        t_rotgrp  *rotg,        /* The rotation group          */
 +        rvec      x[],          /* The positions               */
 +        matrix    box,          /* The simulation box          */
 +        double    t,            /* Time in picoseconds         */
 +        int       step,         /* The time step               */
-         bool      bTorque)
++        gmx_bool  bTorque)
 +{
 +    int       j;
 +    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;
 +    real      fac,fac2,sum;
 +    rvec      pj;
 +
 +    /* For mass weighting: */
 +    real      wj;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +
 +
 +    erg=rotg->enfrotgrp;
 +
 +    /* Clear values from last time step */
 +    erg->V            = 0.0;
 +    sum               = 0.0;
 +    erg->fix_torque_v = 0.0;
 +    erg->fix_angles_v = 0.0;
 +    erg->fix_weight_v = 0.0;
 +
 +    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.(yj-u) */
 +        cprod(rotg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj-u) */
 +
 +                              /*         v x Omega.(yj-u)     */
 +        unitv(tmpvec, pj);    /*  pj = --------------------   */
 +                              /*       | v x Omega.(yj-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 (bTorque)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->fix_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->fix_angles_v += alpha * weight;
 +            erg->fix_weight_v += weight;
 +        }
 +#ifdef INFOF
 +        fprintf(stderr,"RM: step %d node%d FORCE on ATOM %d = (%15.8f %15.8f %15.8f)  torque=%15.8f\n", step, cr->nodeid,
 +                erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ],erg->fix_torque_v);
 +#endif
 +    } /* 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_commrec *cr,
 +        t_rotgrp  *rotg,        /* The rotation group          */
 +        rvec      x[],          /* The positions               */
 +        matrix    box,          /* The simulation box          */
 +        double    t,            /* Time in picoseconds         */
 +        int       step,         /* The time step               */
-         bool      bTorque)
++        gmx_bool  bTorque)
 +{
 +    int       i,ii,iigrp,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;
 +    rvec      qi,qj;
 +
 +    /* For mass weighting: */
 +    real      mj,wi,wj;        /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +
 +
 +    erg=rotg->enfrotgrp;
 +
 +    /* Clear values from last time step */
 +    erg->V            = 0.0;
 +    V                 = 0.0;
 +    erg->fix_torque_v = 0.0;
 +    erg->fix_angles_v = 0.0;
 +    erg->fix_weight_v = 0.0;
 +
 +    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 (bTorque)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->fix_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->fix_angles_v += alpha * weight;
 +            erg->fix_weight_v += weight;
 +        }
 +#ifdef INFOF
 +        fprintf(stderr,"RM-PF: step %d node%d FORCE on ATOM %d = (%15.8f %15.8f %15.8f)  torque=%15.8f\n", step, cr->nodeid,
 +                erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ],erg->fix_torque_v);
 +#endif
 +    } /* 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_commrec *cr,
 +        t_rotgrp  *rotg,        /* The rotation group          */
 +        rvec      x[],          /* The positions               */
 +        matrix    box,          /* The simulation box          */
 +        double    t,            /* Time in picoseconds         */
 +        int       step,         /* The time step               */
-     bool      bPF;
++        gmx_bool  bTorque)
 +{
 +    int       ii,iigrp,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      tmpvec,tmpvec2;
 +    real      fac,fac2,Vpart;
 +    rvec      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 */
-     bool        bFlex,bColl;
++    gmx_bool  bPF;
 +    rvec      innersumvec;
 +
 +
 +    erg=rotg->enfrotgrp;
 +
 +    bPF = rotg->eType==erotgRM2PF;
 +    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);
 +    }
 +
 +    /* Clear values from last time step */
 +    erg->V            = 0.0;
 +    Vpart             = 0.0;
 +    erg->fix_torque_v = 0.0;
 +    erg->fix_angles_v = 0.0;
 +    erg->fix_weight_v = 0.0;
 +
 +    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], tmpvec);     /* tmpvec = yj0 - yc0  */
 +
 +            /* Calculate Omega.(yj0-yc0) */
 +            mvmul(erg->rotmat, tmpvec, 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 (bTorque)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->fix_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->fix_angles_v += alpha * weight;
 +            erg->fix_weight_v += weight;
 +        }
 +#ifdef INFOF
 +        fprintf(stderr,"RM2: step %d node%d FORCE on ATOM %d = (%15.8f %15.8f %15.8f)  torque=%15.8f\n", step, cr->nodeid,
 +                erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ],erg->fix_torque_v);
 +#endif
 +    } /* 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, 
 +        t_commrec *cr)
 +{
 +    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 (MASTER(cr))
 +        fprintf(fplog, "Enforced rotation: allocating memory to store data for %d slabs (rotation group %d).\n",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;
 +}
 +
 +
 +
 +extern void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
 +        rvec *x,gmx_mtop_t *mtop,FILE *out_slabs)
 +{
 +    int i,ii;
 +    rvec        coord,*xdum;
-         bool bNS)
++    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;
 +    
 +
 +    /* Do we have a flexible axis? */
 +    bFlex = (rotg->eType==erotgFLEX) || (rotg->eType==erotgFLEX2);
 +    /* Do we use a global set of coordinates? */
 +    bColl = bFlex || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF);
 +
 +    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);
 +    
 +    /* 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 COM 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 (!bFlex)
 +    {
 +        /* 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, cr);
 +
 +        /* 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,cr,g,-1,out_slabs,TRUE,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);
 +            }
 +        }
 +    }
 +}
 +
 +
 +void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot,t_mdatoms *md)
 +{
 +    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);
 +    }
 +}
 +
 +
 +void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
 +        t_commrec *cr, rvec *x, gmx_mtop_t *mtop, const output_env_t oenv,
 +        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 */
 +
 +
 +    if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
 +        gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
 +
 +    rot = ir->rot;
 +    snew(rot->enfrot, 1);
 +    er=rot->enfrot;
 +    
 +    /* Output every step for reruns */
 +    if (Flags & MD_RERUN)
 +    {
 +        if (fplog)
 +            fprintf(fplog, "Enforced rotation: rerun - will write rotation output every available step.\n");
 +        rot->nstrout = 1;
 +        rot->nsttout = 1;
 +    }
 +
 +    er->out_slabs = NULL;
 +    if (MASTER(cr))
 +        er->out_slabs = open_slab_out(rot, opt2fn("-rs",nfile,fnm));
 +
 +    for(g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +
 +        if (fplog)
 +            fprintf(fplog,"Enforced rotation: group %d type '%s'\n", 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,mtop,er->out_slabs);
 +        }
 +    }
 +    
 +    /* 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 */
 +    er->mpi_bufsize = 4*rot->ngrp; /* To start with */
 +    snew(er->mpi_inbuf , er->mpi_bufsize);
 +    snew(er->mpi_outbuf, er->mpi_bufsize);
 +
 +    /* 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, Flags);
 +        if ( (rotg->eType == erotgFLEX) || (rotg->eType == erotgFLEX2) )
 +        {
 +            if (rot->nstrout > 0)
 +                er->out_angles  = open_angles_out(rot, opt2fn("-ra",nfile,fnm));
 +            if (rot->nsttout > 0)
 +                er->out_torque  = open_torque_out(rot, opt2fn("-rt",nfile,fnm));
 +        }
 +    }
 +}
 +
 +
 +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. 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 already rotated reference position */
 +        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(xref, xcurr, 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,
 +        int step,
 +        gmx_wallcycle_t wcycle,
-     bool     outstep_torque;
-     bool     bFlex,bColl;
++        gmx_bool bNS)
 +{
 +    int      g,i,ii;
 +    t_rot    *rot;
 +    t_rotgrp *rotg;
++    gmx_bool outstep_torque;
++    gmx_bool bFlex,bColl;
 +    float    cycles_rot;
 +    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
 +#ifdef TAKETIME
 +    double t0;
 +#endif
 +    
 +    
 +    rot=ir->rot;
 +    er=rot->enfrot;
 +    
 +    /* At which time steps do we want to output the torque */
 +    outstep_torque = do_per_step(step, rot->nsttout);
 +    
 +    /* Output time into rotation output file */
 +    if (outstep_torque && 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 = (rotg->eType==erotgFLEX) || (rotg->eType==erotgFLEX2);
 +        /* Do we use a collective (global) set of coordinates? */
 +        bColl = bFlex || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF);
 +
 +        /* 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 now ... */
 +    wallcycle_start(wcycle, ewcROT);
 +    GMX_MPE_LOG(ev_rotcycles_start);
 +
 +#ifdef TAKETIME
 +    t0 = MPI_Wtime();
 +#endif
 +
 +    for(g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg=rotg->enfrotgrp;
 +
 +        bFlex = (rotg->eType==erotgFLEX) || (rotg->eType==erotgFLEX2);
 +        bColl = bFlex || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF);
 +
 +        if (outstep_torque && MASTER(cr))
 +            fprintf(er->out_rot, "%12.4f", erg->degangle);
 +
 +        switch(rotg->eType)
 +        {
 +            case erotgISO:
 +            case erotgISOPF:
 +            case erotgPM:
 +            case erotgPMPF:
 +                do_fixed(cr,rotg,x,box,t,step,outstep_torque);
 +                break;
 +            case erotgRM:
 +                do_radial_motion(cr,rotg,x,box,t,step,outstep_torque);
 +                break;
 +            case erotgRMPF:
 +                do_radial_motion_pf(cr,rotg,x,box,t,step,outstep_torque);
 +                break;
 +            case erotgRM2:
 +            case erotgRM2PF:
 +                do_radial_motion2(cr,rotg,x,box,t,step,outstep_torque);
 +                break;
 +            case erotgFLEX:
 +            case erotgFLEX2:
 +                /* Subtract the center of the rotation group */
 +                /* get_center(erg->xc, erg->mc, rotg->nat, center); */
 +                /* svmul(-1.0, center, transvec); */
 +                /* translate_x(erg->xc, rotg->nat, transvec); */
 +                do_flexible(cr,er,rotg,g,x,box,t,step,outstep_torque);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "No such rotation potential.");
 +                break;
 +        }
 +    }
 +
 +#ifdef TAKETIME
 +    if (MASTER(cr))
 +        fprintf(stderr, "Enforced rotation calculation (step %d) took %g seconds.\n", step, MPI_Wtime()-t0);
 +#endif
 +
 +    /* Stop the cycle counter and add to the force cycles for load balancing */
 +    cycles_rot = wallcycle_stop(wcycle,ewcROT);
 +    if (DOMAINDECOMP(cr) && wcycle)
 +        dd_cycles_add(cr->dd,cycles_rot,ddCyclF);
 +    GMX_MPE_LOG(ev_rotcycles_finish);
 +}
Simple merge
index 49df53c3f2df1bdd1c9529a4ba2a52030061a1df,4714d7c73fb90ee4d873d46fe98a09671238f975..da974a4f792f67b86708dfe7b200d61e5bd53949
@@@ -1126,13 -1130,9 +1130,13 @@@ static void make_benchmark_tprs
  
  /* Whether these files are written depends on tpr (or mdp) settings,
   * not on mdrun command line options! */
- static bool tpr_triggers_file(const char *opt)
+ 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