Converted many integrator-loop files to C++
[alexxy/gromacs.git] / src / gromacs / mdlib / coupling.c
diff --git a/src/gromacs/mdlib/coupling.c b/src/gromacs/mdlib/coupling.c
deleted file mode 100644 (file)
index b156c61..0000000
+++ /dev/null
@@ -1,1681 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-#include "gmxpre.h"
-
-#include <assert.h>
-
-#include "gromacs/legacyheaders/macros.h"
-#include "gromacs/legacyheaders/mdrun.h"
-#include "gromacs/legacyheaders/names.h"
-#include "gromacs/legacyheaders/nrnb.h"
-#include "gromacs/legacyheaders/txtdump.h"
-#include "gromacs/legacyheaders/typedefs.h"
-#include "gromacs/legacyheaders/update.h"
-#include "gromacs/legacyheaders/types/commrec.h"
-#include "gromacs/math/units.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/random/random.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/smalloc.h"
-
-#define NTROTTERPARTS 3
-
-/* Suzuki-Yoshida Constants, for n=3 and n=5, for symplectic integration  */
-/* for n=1, w0 = 1 */
-/* for n=3, w0 = w2 = 1/(2-2^-(1/3)), w1 = 1-2*w0 */
-/* for n=5, w0 = w1 = w3 = w4 = 1/(4-4^-(1/3)), w1 = 1-4*w0 */
-
-#define MAX_SUZUKI_YOSHIDA_NUM 5
-#define SUZUKI_YOSHIDA_NUM  5
-
-static const double  sy_const_1[] = { 1. };
-static const double  sy_const_3[] = { 0.828981543588751, -0.657963087177502, 0.828981543588751 };
-static const double  sy_const_5[] = { 0.2967324292201065, 0.2967324292201065, -0.186929716880426, 0.2967324292201065, 0.2967324292201065 };
-
-static const double* sy_const[] = {
-    NULL,
-    sy_const_1,
-    NULL,
-    sy_const_3,
-    NULL,
-    sy_const_5
-};
-
-/*
-   static const double sy_const[MAX_SUZUKI_YOSHIDA_NUM+1][MAX_SUZUKI_YOSHIDA_NUM+1] = {
-    {},
-    {1},
-    {},
-    {0.828981543588751,-0.657963087177502,0.828981543588751},
-    {},
-    {0.2967324292201065,0.2967324292201065,-0.186929716880426,0.2967324292201065,0.2967324292201065}
-   };*/
-
-/* these integration routines are only referenced inside this file */
-static void NHC_trotter(t_grpopts *opts, int nvar, gmx_ekindata_t *ekind, real dtfull,
-                        double xi[], double vxi[], double scalefac[], real *veta, t_extmass *MassQ, gmx_bool bEkinAveVel)
-
-{
-    /* general routine for both barostat and thermostat nose hoover chains */
-
-    int           i, j, mi, mj, jmax;
-    double        Ekin, Efac, reft, kT, nd;
-    double        dt;
-    t_grp_tcstat *tcstat;
-    double       *ivxi, *ixi;
-    double       *iQinv;
-    double       *GQ;
-    gmx_bool      bBarostat;
-    int           mstepsi, mstepsj;
-    int           ns = SUZUKI_YOSHIDA_NUM; /* set the degree of integration in the types/state.h file */
-    int           nh = opts->nhchainlength;
-
-    snew(GQ, nh);
-    mstepsi = mstepsj = ns;
-
-/* if scalefac is NULL, we are doing the NHC of the barostat */
-
-    bBarostat = FALSE;
-    if (scalefac == NULL)
-    {
-        bBarostat = TRUE;
-    }
-
-    for (i = 0; i < nvar; i++)
-    {
-
-        /* make it easier to iterate by selecting
-           out the sub-array that corresponds to this T group */
-
-        ivxi = &vxi[i*nh];
-        ixi  = &xi[i*nh];
-        if (bBarostat)
-        {
-            iQinv = &(MassQ->QPinv[i*nh]);
-            nd    = 1.0; /* THIS WILL CHANGE IF NOT ISOTROPIC */
-            reft  = max(0.0, opts->ref_t[0]);
-            Ekin  = sqr(*veta)/MassQ->Winv;
-        }
-        else
-        {
-            iQinv  = &(MassQ->Qinv[i*nh]);
-            tcstat = &ekind->tcstat[i];
-            nd     = opts->nrdf[i];
-            reft   = max(0.0, opts->ref_t[i]);
-            if (bEkinAveVel)
-            {
-                Ekin = 2*trace(tcstat->ekinf)*tcstat->ekinscalef_nhc;
-            }
-            else
-            {
-                Ekin = 2*trace(tcstat->ekinh)*tcstat->ekinscaleh_nhc;
-            }
-        }
-        kT = BOLTZ*reft;
-
-        for (mi = 0; mi < mstepsi; mi++)
-        {
-            for (mj = 0; mj < mstepsj; mj++)
-            {
-                /* weighting for this step using Suzuki-Yoshida integration - fixed at 5 */
-                dt = sy_const[ns][mj] * dtfull / mstepsi;
-
-                /* compute the thermal forces */
-                GQ[0] = iQinv[0]*(Ekin - nd*kT);
-
-                for (j = 0; j < nh-1; j++)
-                {
-                    if (iQinv[j+1] > 0)
-                    {
-                        /* we actually don't need to update here if we save the
-                           state of the GQ, but it's easier to just recompute*/
-                        GQ[j+1] = iQinv[j+1]*((sqr(ivxi[j])/iQinv[j])-kT);
-                    }
-                    else
-                    {
-                        GQ[j+1] = 0;
-                    }
-                }
-
-                ivxi[nh-1] += 0.25*dt*GQ[nh-1];
-                for (j = nh-1; j > 0; j--)
-                {
-                    Efac      = exp(-0.125*dt*ivxi[j]);
-                    ivxi[j-1] = Efac*(ivxi[j-1]*Efac + 0.25*dt*GQ[j-1]);
-                }
-
-                Efac = exp(-0.5*dt*ivxi[0]);
-                if (bBarostat)
-                {
-                    *veta *= Efac;
-                }
-                else
-                {
-                    scalefac[i] *= Efac;
-                }
-                Ekin *= (Efac*Efac);
-
-                /* Issue - if the KE is an average of the last and the current temperatures, then we might not be
-                   able to scale the kinetic energy directly with this factor.  Might take more bookkeeping -- have to
-                   think about this a bit more . . . */
-
-                GQ[0] = iQinv[0]*(Ekin - nd*kT);
-
-                /* update thermostat positions */
-                for (j = 0; j < nh; j++)
-                {
-                    ixi[j] += 0.5*dt*ivxi[j];
-                }
-
-                for (j = 0; j < nh-1; j++)
-                {
-                    Efac    = exp(-0.125*dt*ivxi[j+1]);
-                    ivxi[j] = Efac*(ivxi[j]*Efac + 0.25*dt*GQ[j]);
-                    if (iQinv[j+1] > 0)
-                    {
-                        GQ[j+1] = iQinv[j+1]*((sqr(ivxi[j])/iQinv[j])-kT);
-                    }
-                    else
-                    {
-                        GQ[j+1] = 0;
-                    }
-                }
-                ivxi[nh-1] += 0.25*dt*GQ[nh-1];
-            }
-        }
-    }
-    sfree(GQ);
-}
-
-static void boxv_trotter(t_inputrec *ir, real *veta, real dt, tensor box,
-                         gmx_ekindata_t *ekind, tensor vir, real pcorr, t_extmass *MassQ)
-{
-
-    real   pscal;
-    double alpha;
-    int    i, j, d, n, nwall;
-    real   T, GW, vol;
-    tensor Winvm, ekinmod, localpres;
-
-    /* The heat bath is coupled to a separate barostat, the last temperature group.  In the
-       2006 Tuckerman et al paper., the order is iL_{T_baro} iL {T_part}
-     */
-
-    if (ir->epct == epctSEMIISOTROPIC)
-    {
-        nwall = 2;
-    }
-    else
-    {
-        nwall = 3;
-    }
-
-    /* eta is in pure units.  veta is in units of ps^-1. GW is in
-       units of ps^-2.  However, eta has a reference of 1 nm^3, so care must be
-       taken to use only RATIOS of eta in updating the volume. */
-
-    /* we take the partial pressure tensors, modify the
-       kinetic energy tensor, and recovert to pressure */
-
-    if (ir->opts.nrdf[0] == 0)
-    {
-        gmx_fatal(FARGS, "Barostat is coupled to a T-group with no degrees of freedom\n");
-    }
-    /* alpha factor for phase space volume, then multiply by the ekin scaling factor.  */
-    alpha  = 1.0 + DIM/((double)ir->opts.nrdf[0]);
-    alpha *= ekind->tcstat[0].ekinscalef_nhc;
-    msmul(ekind->ekin, alpha, ekinmod);
-    /* for now, we use Elr = 0, because if you want to get it right, you
-       really should be using PME. Maybe print a warning? */
-
-    pscal   = calc_pres(ir->ePBC, nwall, box, ekinmod, vir, localpres)+pcorr;
-
-    vol = det(box);
-    GW  = (vol*(MassQ->Winv/PRESFAC))*(DIM*pscal - trace(ir->ref_p));  /* W is in ps^2 * bar * nm^3 */
-
-    *veta += 0.5*dt*GW;
-}
-
-/*
- * This file implements temperature and pressure coupling algorithms:
- * For now only the Weak coupling and the modified weak coupling.
- *
- * Furthermore computation of pressure and temperature is done here
- *
- */
-
-real calc_pres(int ePBC, int nwall, matrix box, tensor ekin, tensor vir,
-               tensor pres)
-{
-    int  n, m;
-    real fac;
-
-    if (ePBC == epbcNONE || (ePBC == epbcXY && nwall != 2))
-    {
-        clear_mat(pres);
-    }
-    else
-    {
-        /* Uitzoeken welke ekin hier van toepassing is, zie Evans & Morris - E.
-         * Wrs. moet de druktensor gecorrigeerd worden voor de netto stroom in
-         * het systeem...
-         */
-
-        fac = PRESFAC*2.0/det(box);
-        for (n = 0; (n < DIM); n++)
-        {
-            for (m = 0; (m < DIM); m++)
-            {
-                pres[n][m] = (ekin[n][m] - vir[n][m])*fac;
-            }
-        }
-
-        if (debug)
-        {
-            pr_rvecs(debug, 0, "PC: pres", pres, DIM);
-            pr_rvecs(debug, 0, "PC: ekin", ekin, DIM);
-            pr_rvecs(debug, 0, "PC: vir ", vir, DIM);
-            pr_rvecs(debug, 0, "PC: box ", box, DIM);
-        }
-    }
-    return trace(pres)/DIM;
-}
-
-real calc_temp(real ekin, real nrdf)
-{
-    if (nrdf > 0)
-    {
-        return (2.0*ekin)/(nrdf*BOLTZ);
-    }
-    else
-    {
-        return 0;
-    }
-}
-
-void parrinellorahman_pcoupl(FILE *fplog, gmx_int64_t step,
-                             t_inputrec *ir, real dt, tensor pres,
-                             tensor box, tensor box_rel, tensor boxv,
-                             tensor M, matrix mu, gmx_bool bFirstStep)
-{
-    /* This doesn't do any coordinate updating. It just
-     * integrates the box vector equations from the calculated
-     * acceleration due to pressure difference. We also compute
-     * the tensor M which is used in update to couple the particle
-     * coordinates to the box vectors.
-     *
-     * In Nose and Klein (Mol.Phys 50 (1983) no 5., p 1055) this is
-     * given as
-     *            -1    .           .     -1
-     * M_nk = (h')   * (h' * h + h' h) * h
-     *
-     * with the dots denoting time derivatives and h is the transformation from
-     * the scaled frame to the real frame, i.e. the TRANSPOSE of the box.
-     * This also goes for the pressure and M tensors - they are transposed relative
-     * to ours. Our equation thus becomes:
-     *
-     *                  -1       .    .           -1
-     * M_gmx = M_nk' = b  * (b * b' + b * b') * b'
-     *
-     * where b is the gromacs box matrix.
-     * Our box accelerations are given by
-     *   ..                                    ..
-     *   b = vol/W inv(box') * (P-ref_P)     (=h')
-     */
-
-    int    d, n;
-    tensor winv;
-    real   vol = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
-    real   atot, arel, change, maxchange, xy_pressure;
-    tensor invbox, pdiff, t1, t2;
-
-    real   maxl;
-
-    m_inv_ur0(box, invbox);
-
-    if (!bFirstStep)
-    {
-        /* Note that PRESFAC does not occur here.
-         * The pressure and compressibility always occur as a product,
-         * therefore the pressure unit drops out.
-         */
-        maxl = max(box[XX][XX], box[YY][YY]);
-        maxl = max(maxl, box[ZZ][ZZ]);
-        for (d = 0; d < DIM; d++)
-        {
-            for (n = 0; n < DIM; n++)
-            {
-                winv[d][n] =
-                    (4*M_PI*M_PI*ir->compress[d][n])/(3*ir->tau_p*ir->tau_p*maxl);
-            }
-        }
-
-        m_sub(pres, ir->ref_p, pdiff);
-
-        if (ir->epct == epctSURFACETENSION)
-        {
-            /* Unlike Berendsen coupling it might not be trivial to include a z
-             * pressure correction here? On the other hand we don't scale the
-             * box momentarily, but change accelerations, so it might not be crucial.
-             */
-            xy_pressure = 0.5*(pres[XX][XX]+pres[YY][YY]);
-            for (d = 0; d < ZZ; d++)
-            {
-                pdiff[d][d] = (xy_pressure-(pres[ZZ][ZZ]-ir->ref_p[d][d]/box[d][d]));
-            }
-        }
-
-        tmmul(invbox, pdiff, t1);
-        /* Move the off-diagonal elements of the 'force' to one side to ensure
-         * that we obey the box constraints.
-         */
-        for (d = 0; d < DIM; d++)
-        {
-            for (n = 0; n < d; n++)
-            {
-                t1[d][n] += t1[n][d];
-                t1[n][d]  = 0;
-            }
-        }
-
-        switch (ir->epct)
-        {
-            case epctANISOTROPIC:
-                for (d = 0; d < DIM; d++)
-                {
-                    for (n = 0; n <= d; n++)
-                    {
-                        t1[d][n] *= winv[d][n]*vol;
-                    }
-                }
-                break;
-            case epctISOTROPIC:
-                /* calculate total volume acceleration */
-                atot = box[XX][XX]*box[YY][YY]*t1[ZZ][ZZ]+
-                    box[XX][XX]*t1[YY][YY]*box[ZZ][ZZ]+
-                    t1[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
-                arel = atot/(3*vol);
-                /* set all RELATIVE box accelerations equal, and maintain total V
-                 * change speed */
-                for (d = 0; d < DIM; d++)
-                {
-                    for (n = 0; n <= d; n++)
-                    {
-                        t1[d][n] = winv[0][0]*vol*arel*box[d][n];
-                    }
-                }
-                break;
-            case epctSEMIISOTROPIC:
-            case epctSURFACETENSION:
-                /* Note the correction to pdiff above for surftens. coupling  */
-
-                /* calculate total XY volume acceleration */
-                atot = box[XX][XX]*t1[YY][YY]+t1[XX][XX]*box[YY][YY];
-                arel = atot/(2*box[XX][XX]*box[YY][YY]);
-                /* set RELATIVE XY box accelerations equal, and maintain total V
-                 * change speed. Dont change the third box vector accelerations */
-                for (d = 0; d < ZZ; d++)
-                {
-                    for (n = 0; n <= d; n++)
-                    {
-                        t1[d][n] = winv[d][n]*vol*arel*box[d][n];
-                    }
-                }
-                for (n = 0; n < DIM; n++)
-                {
-                    t1[ZZ][n] *= winv[d][n]*vol;
-                }
-                break;
-            default:
-                gmx_fatal(FARGS, "Parrinello-Rahman pressure coupling type %s "
-                          "not supported yet\n", EPCOUPLTYPETYPE(ir->epct));
-                break;
-        }
-
-        maxchange = 0;
-        for (d = 0; d < DIM; d++)
-        {
-            for (n = 0; n <= d; n++)
-            {
-                boxv[d][n] += dt*t1[d][n];
-
-                /* We do NOT update the box vectors themselves here, since
-                 * we need them for shifting later. It is instead done last
-                 * in the update() routine.
-                 */
-
-                /* Calculate the change relative to diagonal elements-
-                   since it's perfectly ok for the off-diagonal ones to
-                   be zero it doesn't make sense to check the change relative
-                   to its current size.
-                 */
-
-                change = fabs(dt*boxv[d][n]/box[d][d]);
-
-                if (change > maxchange)
-                {
-                    maxchange = change;
-                }
-            }
-        }
-
-        if (maxchange > 0.01 && fplog)
-        {
-            char buf[22];
-            fprintf(fplog,
-                    "\nStep %s  Warning: Pressure scaling more than 1%%. "
-                    "This may mean your system\n is not yet equilibrated. "
-                    "Use of Parrinello-Rahman pressure coupling during\n"
-                    "equilibration can lead to simulation instability, "
-                    "and is discouraged.\n",
-                    gmx_step_str(step, buf));
-        }
-    }
-
-    preserve_box_shape(ir, box_rel, boxv);
-
-    mtmul(boxv, box, t1);   /* t1=boxv * b' */
-    mmul(invbox, t1, t2);
-    mtmul(t2, invbox, M);
-
-    /* Determine the scaling matrix mu for the coordinates */
-    for (d = 0; d < DIM; d++)
-    {
-        for (n = 0; n <= d; n++)
-        {
-            t1[d][n] = box[d][n] + dt*boxv[d][n];
-        }
-    }
-    preserve_box_shape(ir, box_rel, t1);
-    /* t1 is the box at t+dt, determine mu as the relative change */
-    mmul_ur0(invbox, t1, mu);
-}
-
-void berendsen_pcoupl(FILE *fplog, gmx_int64_t step,
-                      t_inputrec *ir, real dt, tensor pres, matrix box,
-                      matrix mu)
-{
-    int     d, n;
-    real    scalar_pressure, xy_pressure, p_corr_z;
-    char   *ptr, buf[STRLEN];
-
-    /*
-     *  Calculate the scaling matrix mu
-     */
-    scalar_pressure = 0;
-    xy_pressure     = 0;
-    for (d = 0; d < DIM; d++)
-    {
-        scalar_pressure += pres[d][d]/DIM;
-        if (d != ZZ)
-        {
-            xy_pressure += pres[d][d]/(DIM-1);
-        }
-    }
-    /* Pressure is now in bar, everywhere. */
-#define factor(d, m) (ir->compress[d][m]*dt/ir->tau_p)
-
-    /* mu has been changed from pow(1+...,1/3) to 1+.../3, since this is
-     * necessary for triclinic scaling
-     */
-    clear_mat(mu);
-    switch (ir->epct)
-    {
-        case epctISOTROPIC:
-            for (d = 0; d < DIM; d++)
-            {
-                mu[d][d] = 1.0 - factor(d, d)*(ir->ref_p[d][d] - scalar_pressure) /DIM;
-            }
-            break;
-        case epctSEMIISOTROPIC:
-            for (d = 0; d < ZZ; d++)
-            {
-                mu[d][d] = 1.0 - factor(d, d)*(ir->ref_p[d][d]-xy_pressure)/DIM;
-            }
-            mu[ZZ][ZZ] =
-                1.0 - factor(ZZ, ZZ)*(ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ])/DIM;
-            break;
-        case epctANISOTROPIC:
-            for (d = 0; d < DIM; d++)
-            {
-                for (n = 0; n < DIM; n++)
-                {
-                    mu[d][n] = (d == n ? 1.0 : 0.0)
-                        -factor(d, n)*(ir->ref_p[d][n] - pres[d][n])/DIM;
-                }
-            }
-            break;
-        case epctSURFACETENSION:
-            /* ir->ref_p[0/1] is the reference surface-tension times *
-             * the number of surfaces                                */
-            if (ir->compress[ZZ][ZZ])
-            {
-                p_corr_z = dt/ir->tau_p*(ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ]);
-            }
-            else
-            {
-                /* when the compressibity is zero, set the pressure correction   *
-                 * in the z-direction to zero to get the correct surface tension */
-                p_corr_z = 0;
-            }
-            mu[ZZ][ZZ] = 1.0 - ir->compress[ZZ][ZZ]*p_corr_z;
-            for (d = 0; d < DIM-1; d++)
-            {
-                mu[d][d] = 1.0 + factor(d, d)*(ir->ref_p[d][d]/(mu[ZZ][ZZ]*box[ZZ][ZZ])
-                                               - (pres[ZZ][ZZ]+p_corr_z - xy_pressure))/(DIM-1);
-            }
-            break;
-        default:
-            gmx_fatal(FARGS, "Berendsen pressure coupling type %s not supported yet\n",
-                      EPCOUPLTYPETYPE(ir->epct));
-            break;
-    }
-    /* To fullfill the orientation restrictions on triclinic boxes
-     * we will set mu_yx, mu_zx and mu_zy to 0 and correct
-     * the other elements of mu to first order.
-     */
-    mu[YY][XX] += mu[XX][YY];
-    mu[ZZ][XX] += mu[XX][ZZ];
-    mu[ZZ][YY] += mu[YY][ZZ];
-    mu[XX][YY]  = 0;
-    mu[XX][ZZ]  = 0;
-    mu[YY][ZZ]  = 0;
-
-    if (debug)
-    {
-        pr_rvecs(debug, 0, "PC: pres ", pres, 3);
-        pr_rvecs(debug, 0, "PC: mu   ", mu, 3);
-    }
-
-    if (mu[XX][XX] < 0.99 || mu[XX][XX] > 1.01 ||
-        mu[YY][YY] < 0.99 || mu[YY][YY] > 1.01 ||
-        mu[ZZ][ZZ] < 0.99 || mu[ZZ][ZZ] > 1.01)
-    {
-        char buf2[22];
-        sprintf(buf, "\nStep %s  Warning: pressure scaling more than 1%%, "
-                "mu: %g %g %g\n",
-                gmx_step_str(step, buf2), mu[XX][XX], mu[YY][YY], mu[ZZ][ZZ]);
-        if (fplog)
-        {
-            fprintf(fplog, "%s", buf);
-        }
-        fprintf(stderr, "%s", buf);
-    }
-}
-
-void berendsen_pscale(t_inputrec *ir, matrix mu,
-                      matrix box, matrix box_rel,
-                      int start, int nr_atoms,
-                      rvec x[], unsigned short cFREEZE[],
-                      t_nrnb *nrnb)
-{
-    ivec   *nFreeze = ir->opts.nFreeze;
-    int     n, d, g = 0;
-
-    /* Scale the positions */
-    for (n = start; n < start+nr_atoms; n++)
-    {
-        if (cFREEZE)
-        {
-            g = cFREEZE[n];
-        }
-
-        if (!nFreeze[g][XX])
-        {
-            x[n][XX] = mu[XX][XX]*x[n][XX]+mu[YY][XX]*x[n][YY]+mu[ZZ][XX]*x[n][ZZ];
-        }
-        if (!nFreeze[g][YY])
-        {
-            x[n][YY] = mu[YY][YY]*x[n][YY]+mu[ZZ][YY]*x[n][ZZ];
-        }
-        if (!nFreeze[g][ZZ])
-        {
-            x[n][ZZ] = mu[ZZ][ZZ]*x[n][ZZ];
-        }
-    }
-    /* compute final boxlengths */
-    for (d = 0; d < DIM; d++)
-    {
-        box[d][XX] = mu[XX][XX]*box[d][XX]+mu[YY][XX]*box[d][YY]+mu[ZZ][XX]*box[d][ZZ];
-        box[d][YY] = mu[YY][YY]*box[d][YY]+mu[ZZ][YY]*box[d][ZZ];
-        box[d][ZZ] = mu[ZZ][ZZ]*box[d][ZZ];
-    }
-
-    preserve_box_shape(ir, box_rel, box);
-
-    /* (un)shifting should NOT be done after this,
-     * since the box vectors might have changed
-     */
-    inc_nrnb(nrnb, eNR_PCOUPL, nr_atoms);
-}
-
-void berendsen_tcoupl(t_inputrec *ir, gmx_ekindata_t *ekind, real dt)
-{
-    t_grpopts *opts;
-    int        i;
-    real       T, reft = 0, lll;
-
-    opts = &ir->opts;
-
-    for (i = 0; (i < opts->ngtc); i++)
-    {
-        if (ir->eI == eiVV)
-        {
-            T = ekind->tcstat[i].T;
-        }
-        else
-        {
-            T = ekind->tcstat[i].Th;
-        }
-
-        if ((opts->tau_t[i] > 0) && (T > 0.0))
-        {
-            reft                    = max(0.0, opts->ref_t[i]);
-            lll                     = sqrt(1.0 + (dt/opts->tau_t[i])*(reft/T-1.0));
-            ekind->tcstat[i].lambda = max(min(lll, 1.25), 0.8);
-        }
-        else
-        {
-            ekind->tcstat[i].lambda = 1.0;
-        }
-
-        if (debug)
-        {
-            fprintf(debug, "TC: group %d: T: %g, Lambda: %g\n",
-                    i, T, ekind->tcstat[i].lambda);
-        }
-    }
-}
-
-void andersen_tcoupl(t_inputrec *ir, gmx_int64_t step,
-                     const t_commrec *cr, const t_mdatoms *md, t_state *state, real rate, const gmx_bool *randomize, const real *boltzfac)
-{
-    const int *gatindex = (DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
-    int        i;
-    int        gc = 0;
-
-    /* randomize the velocities of the selected particles */
-
-    for (i = 0; i < md->homenr; i++)  /* now loop over the list of atoms */
-    {
-        int      ng = gatindex ? gatindex[i] : i;
-        gmx_bool bRandomize;
-
-        if (md->cTC)
-        {
-            gc = md->cTC[i];  /* assign the atom to a temperature group if there are more than one */
-        }
-        if (randomize[gc])
-        {
-            if (ir->etc == etcANDERSEN)
-            {
-                bRandomize = TRUE;
-            }
-            else
-            {
-                double uniform[2];
-
-                gmx_rng_cycle_2uniform(step*2, ng, ir->andersen_seed, RND_SEED_ANDERSEN, uniform);
-                bRandomize = (uniform[0] < rate);
-            }
-            if (bRandomize)
-            {
-                real scal, gauss[3];
-                int  d;
-
-                scal = sqrt(boltzfac[gc]*md->invmass[i]);
-                gmx_rng_cycle_3gaussian_table(step*2+1, ng, ir->andersen_seed, RND_SEED_ANDERSEN, gauss);
-                for (d = 0; d < DIM; d++)
-                {
-                    state->v[i][d] = scal*gauss[d];
-                }
-            }
-        }
-    }
-}
-
-
-void nosehoover_tcoupl(t_grpopts *opts, gmx_ekindata_t *ekind, real dt,
-                       double xi[], double vxi[], t_extmass *MassQ)
-{
-    int   i;
-    real  reft, oldvxi;
-
-    /* note that this routine does not include Nose-hoover chains yet. Should be easy to add. */
-
-    for (i = 0; (i < opts->ngtc); i++)
-    {
-        reft     = max(0.0, opts->ref_t[i]);
-        oldvxi   = vxi[i];
-        vxi[i]  += dt*MassQ->Qinv[i]*(ekind->tcstat[i].Th - reft);
-        xi[i]   += dt*(oldvxi + vxi[i])*0.5;
-    }
-}
-
-t_state *init_bufstate(const t_state *template_state)
-{
-    t_state *state;
-    int      nc = template_state->nhchainlength;
-    snew(state, 1);
-    snew(state->nosehoover_xi, nc*template_state->ngtc);
-    snew(state->nosehoover_vxi, nc*template_state->ngtc);
-    snew(state->therm_integral, template_state->ngtc);
-    snew(state->nhpres_xi, nc*template_state->nnhpres);
-    snew(state->nhpres_vxi, nc*template_state->nnhpres);
-
-    return state;
-}
-
-void destroy_bufstate(t_state *state)
-{
-    sfree(state->x);
-    sfree(state->v);
-    sfree(state->nosehoover_xi);
-    sfree(state->nosehoover_vxi);
-    sfree(state->therm_integral);
-    sfree(state->nhpres_xi);
-    sfree(state->nhpres_vxi);
-    sfree(state);
-}
-
-void trotter_update(t_inputrec *ir, gmx_int64_t step, gmx_ekindata_t *ekind,
-                    gmx_enerdata_t *enerd, t_state *state,
-                    tensor vir, t_mdatoms *md,
-                    t_extmass *MassQ, int **trotter_seqlist, int trotter_seqno)
-{
-
-    int             n, i, j, d, ntgrp, ngtc, gc = 0;
-    t_grp_tcstat   *tcstat;
-    t_grpopts      *opts;
-    gmx_int64_t     step_eff;
-    real            ecorr, pcorr, dvdlcorr;
-    real            bmass, qmass, reft, kT, dt, nd;
-    tensor          dumpres, dumvir;
-    double         *scalefac, dtc;
-    int            *trotter_seq;
-    rvec            sumv = {0, 0, 0}, consk;
-    gmx_bool        bCouple;
-
-    if (trotter_seqno <= ettTSEQ2)
-    {
-        step_eff = step-1;  /* the velocity verlet calls are actually out of order -- the first half step
-                               is actually the last half step from the previous step.  Thus the first half step
-                               actually corresponds to the n-1 step*/
-
-    }
-    else
-    {
-        step_eff = step;
-    }
-
-    bCouple = (ir->nsttcouple == 1 ||
-               do_per_step(step_eff+ir->nsttcouple, ir->nsttcouple));
-
-    trotter_seq = trotter_seqlist[trotter_seqno];
-
-    if ((trotter_seq[0] == etrtSKIPALL) || (!bCouple))
-    {
-        return;
-    }
-    dtc  = ir->nsttcouple*ir->delta_t; /* This is OK for NPT, because nsttcouple == nstpcouple is enforcesd */
-    opts = &(ir->opts);                /* just for ease of referencing */
-    ngtc = opts->ngtc;
-    assert(ngtc > 0);
-    snew(scalefac, opts->ngtc);
-    for (i = 0; i < ngtc; i++)
-    {
-        scalefac[i] = 1;
-    }
-    /* execute the series of trotter updates specified in the trotterpart array */
-
-    for (i = 0; i < NTROTTERPARTS; i++)
-    {
-        /* allow for doubled intgrators by doubling dt instead of making 2 calls */
-        if ((trotter_seq[i] == etrtBAROV2) || (trotter_seq[i] == etrtBARONHC2) || (trotter_seq[i] == etrtNHC2))
-        {
-            dt = 2 * dtc;
-        }
-        else
-        {
-            dt = dtc;
-        }
-
-        switch (trotter_seq[i])
-        {
-            case etrtBAROV:
-            case etrtBAROV2:
-                boxv_trotter(ir, &(state->veta), dt, state->box, ekind, vir,
-                             enerd->term[F_PDISPCORR], MassQ);
-                break;
-            case etrtBARONHC:
-            case etrtBARONHC2:
-                NHC_trotter(opts, state->nnhpres, ekind, dt, state->nhpres_xi,
-                            state->nhpres_vxi, NULL, &(state->veta), MassQ, FALSE);
-                break;
-            case etrtNHC:
-            case etrtNHC2:
-                NHC_trotter(opts, opts->ngtc, ekind, dt, state->nosehoover_xi,
-                            state->nosehoover_vxi, scalefac, NULL, MassQ, (ir->eI == eiVV));
-                /* need to rescale the kinetic energies and velocities here.  Could
-                   scale the velocities later, but we need them scaled in order to
-                   produce the correct outputs, so we'll scale them here. */
-
-                for (i = 0; i < ngtc; i++)
-                {
-                    tcstat                  = &ekind->tcstat[i];
-                    tcstat->vscale_nhc      = scalefac[i];
-                    tcstat->ekinscaleh_nhc *= (scalefac[i]*scalefac[i]);
-                    tcstat->ekinscalef_nhc *= (scalefac[i]*scalefac[i]);
-                }
-                /* now that we've scaled the groupwise velocities, we can add them up to get the total */
-                /* but do we actually need the total? */
-
-                /* modify the velocities as well */
-                for (n = 0; n < md->homenr; n++)
-                {
-                    if (md->cTC) /* does this conditional need to be here? is this always true?*/
-                    {
-                        gc = md->cTC[n];
-                    }
-                    for (d = 0; d < DIM; d++)
-                    {
-                        state->v[n][d] *= scalefac[gc];
-                    }
-
-                    if (debug)
-                    {
-                        for (d = 0; d < DIM; d++)
-                        {
-                            sumv[d] += (state->v[n][d])/md->invmass[n];
-                        }
-                    }
-                }
-                break;
-            default:
-                break;
-        }
-    }
-    /* check for conserved momentum -- worth looking at this again eventually, but not working right now.*/
-#if 0
-    if (debug)
-    {
-        if (bFirstHalf)
-        {
-            for (d = 0; d < DIM; d++)
-            {
-                consk[d] = sumv[d]*exp((1 + 1.0/opts->nrdf[0])*((1.0/DIM)*log(det(state->box)/state->vol0)) + state->nosehoover_xi[0]);
-            }
-            fprintf(debug, "Conserved kappa: %15.8f %15.8f %15.8f\n", consk[0], consk[1], consk[2]);
-        }
-    }
-#endif
-    sfree(scalefac);
-}
-
-
-extern void init_npt_masses(t_inputrec *ir, t_state *state, t_extmass *MassQ, gmx_bool bInit)
-{
-    int           n, i, j, d, ntgrp, ngtc, nnhpres, nh, gc = 0;
-    t_grp_tcstat *tcstat;
-    t_grpopts    *opts;
-    real          ecorr, pcorr, dvdlcorr;
-    real          bmass, qmass, reft, kT, dt, ndj, nd;
-    tensor        dumpres, dumvir;
-
-    opts    = &(ir->opts); /* just for ease of referencing */
-    ngtc    = ir->opts.ngtc;
-    nnhpres = state->nnhpres;
-    nh      = state->nhchainlength;
-
-    if (ir->eI == eiMD)
-    {
-        if (bInit)
-        {
-            snew(MassQ->Qinv, ngtc);
-        }
-        for (i = 0; (i < ngtc); i++)
-        {
-            if ((opts->tau_t[i] > 0) && (opts->ref_t[i] > 0))
-            {
-                MassQ->Qinv[i] = 1.0/(sqr(opts->tau_t[i]/M_2PI)*opts->ref_t[i]);
-            }
-            else
-            {
-                MassQ->Qinv[i] = 0.0;
-            }
-        }
-    }
-    else if (EI_VV(ir->eI))
-    {
-        /* Set pressure variables */
-
-        if (bInit)
-        {
-            if (state->vol0 == 0)
-            {
-                state->vol0 = det(state->box);
-                /* because we start by defining a fixed
-                   compressibility, we need the volume at this
-                   compressibility to solve the problem. */
-            }
-        }
-
-        /* units are nm^3 * ns^2 / (nm^3 * bar / kJ/mol) = kJ/mol  */
-        /* Consider evaluating eventually if this the right mass to use.  All are correct, some might be more stable  */
-        MassQ->Winv = (PRESFAC*trace(ir->compress)*BOLTZ*opts->ref_t[0])/(DIM*state->vol0*sqr(ir->tau_p/M_2PI));
-        /* An alternate mass definition, from Tuckerman et al. */
-        /* MassQ->Winv = 1.0/(sqr(ir->tau_p/M_2PI)*(opts->nrdf[0]+DIM)*BOLTZ*opts->ref_t[0]); */
-        for (d = 0; d < DIM; d++)
-        {
-            for (n = 0; n < DIM; n++)
-            {
-                MassQ->Winvm[d][n] = PRESFAC*ir->compress[d][n]/(state->vol0*sqr(ir->tau_p/M_2PI));
-                /* not clear this is correct yet for the anisotropic case. Will need to reevaluate
-                   before using MTTK for anisotropic states.*/
-            }
-        }
-        /* Allocate space for thermostat variables */
-        if (bInit)
-        {
-            snew(MassQ->Qinv, ngtc*nh);
-        }
-
-        /* now, set temperature variables */
-        for (i = 0; i < ngtc; i++)
-        {
-            if ((opts->tau_t[i] > 0) && (opts->ref_t[i] > 0))
-            {
-                reft = max(0.0, opts->ref_t[i]);
-                nd   = opts->nrdf[i];
-                kT   = BOLTZ*reft;
-                for (j = 0; j < nh; j++)
-                {
-                    if (j == 0)
-                    {
-                        ndj = nd;
-                    }
-                    else
-                    {
-                        ndj = 1;
-                    }
-                    MassQ->Qinv[i*nh+j]   = 1.0/(sqr(opts->tau_t[i]/M_2PI)*ndj*kT);
-                }
-            }
-            else
-            {
-                reft = 0.0;
-                for (j = 0; j < nh; j++)
-                {
-                    MassQ->Qinv[i*nh+j] = 0.0;
-                }
-            }
-        }
-    }
-}
-
-int **init_npt_vars(t_inputrec *ir, t_state *state, t_extmass *MassQ, gmx_bool bTrotter)
-{
-    int           n, i, j, d, ntgrp, ngtc, nnhpres, nh, gc = 0;
-    t_grp_tcstat *tcstat;
-    t_grpopts    *opts;
-    real          ecorr, pcorr, dvdlcorr;
-    real          bmass, qmass, reft, kT, dt, ndj, nd;
-    tensor        dumpres, dumvir;
-    int         **trotter_seq;
-
-    opts    = &(ir->opts); /* just for ease of referencing */
-    ngtc    = state->ngtc;
-    nnhpres = state->nnhpres;
-    nh      = state->nhchainlength;
-
-    init_npt_masses(ir, state, MassQ, TRUE);
-
-    /* first, initialize clear all the trotter calls */
-    snew(trotter_seq, ettTSEQMAX);
-    for (i = 0; i < ettTSEQMAX; i++)
-    {
-        snew(trotter_seq[i], NTROTTERPARTS);
-        for (j = 0; j < NTROTTERPARTS; j++)
-        {
-            trotter_seq[i][j] = etrtNONE;
-        }
-        trotter_seq[i][0] = etrtSKIPALL;
-    }
-
-    if (!bTrotter)
-    {
-        /* no trotter calls, so we never use the values in the array.
-         * We access them (so we need to define them, but ignore
-         * then.*/
-
-        return trotter_seq;
-    }
-
-    /* compute the kinetic energy by using the half step velocities or
-     * the kinetic energies, depending on the order of the trotter calls */
-
-    if (ir->eI == eiVV)
-    {
-        if (IR_NPT_TROTTER(ir))
-        {
-            /* This is the complicated version - there are 4 possible calls, depending on ordering.
-               We start with the initial one. */
-            /* first, a round that estimates veta. */
-            trotter_seq[0][0] = etrtBAROV;
-
-            /* trotter_seq[1] is etrtNHC for 1/2 step velocities - leave zero */
-
-            /* The first half trotter update */
-            trotter_seq[2][0] = etrtBAROV;
-            trotter_seq[2][1] = etrtNHC;
-            trotter_seq[2][2] = etrtBARONHC;
-
-            /* The second half trotter update */
-            trotter_seq[3][0] = etrtBARONHC;
-            trotter_seq[3][1] = etrtNHC;
-            trotter_seq[3][2] = etrtBAROV;
-
-            /* trotter_seq[4] is etrtNHC for second 1/2 step velocities - leave zero */
-
-        }
-        else if (IR_NVT_TROTTER(ir))
-        {
-            /* This is the easy version - there are only two calls, both the same.
-               Otherwise, even easier -- no calls  */
-            trotter_seq[2][0] = etrtNHC;
-            trotter_seq[3][0] = etrtNHC;
-        }
-        else if (IR_NPH_TROTTER(ir))
-        {
-            /* This is the complicated version - there are 4 possible calls, depending on ordering.
-               We start with the initial one. */
-            /* first, a round that estimates veta. */
-            trotter_seq[0][0] = etrtBAROV;
-
-            /* trotter_seq[1] is etrtNHC for 1/2 step velocities - leave zero */
-
-            /* The first half trotter update */
-            trotter_seq[2][0] = etrtBAROV;
-            trotter_seq[2][1] = etrtBARONHC;
-
-            /* The second half trotter update */
-            trotter_seq[3][0] = etrtBARONHC;
-            trotter_seq[3][1] = etrtBAROV;
-
-            /* trotter_seq[4] is etrtNHC for second 1/2 step velocities - leave zero */
-        }
-    }
-    else if (ir->eI == eiVVAK)
-    {
-        if (IR_NPT_TROTTER(ir))
-        {
-            /* This is the complicated version - there are 4 possible calls, depending on ordering.
-               We start with the initial one. */
-            /* first, a round that estimates veta. */
-            trotter_seq[0][0] = etrtBAROV;
-
-            /* The first half trotter update, part 1 -- double update, because it commutes */
-            trotter_seq[1][0] = etrtNHC;
-
-            /* The first half trotter update, part 2 */
-            trotter_seq[2][0] = etrtBAROV;
-            trotter_seq[2][1] = etrtBARONHC;
-
-            /* The second half trotter update, part 1 */
-            trotter_seq[3][0] = etrtBARONHC;
-            trotter_seq[3][1] = etrtBAROV;
-
-            /* The second half trotter update */
-            trotter_seq[4][0] = etrtNHC;
-        }
-        else if (IR_NVT_TROTTER(ir))
-        {
-            /* This is the easy version - there is only one call, both the same.
-               Otherwise, even easier -- no calls  */
-            trotter_seq[1][0] = etrtNHC;
-            trotter_seq[4][0] = etrtNHC;
-        }
-        else if (IR_NPH_TROTTER(ir))
-        {
-            /* This is the complicated version - there are 4 possible calls, depending on ordering.
-               We start with the initial one. */
-            /* first, a round that estimates veta. */
-            trotter_seq[0][0] = etrtBAROV;
-
-            /* The first half trotter update, part 1 -- leave zero */
-            trotter_seq[1][0] = etrtNHC;
-
-            /* The first half trotter update, part 2 */
-            trotter_seq[2][0] = etrtBAROV;
-            trotter_seq[2][1] = etrtBARONHC;
-
-            /* The second half trotter update, part 1 */
-            trotter_seq[3][0] = etrtBARONHC;
-            trotter_seq[3][1] = etrtBAROV;
-
-            /* The second half trotter update -- blank for now */
-        }
-    }
-
-    switch (ir->epct)
-    {
-        case epctISOTROPIC:
-        default:
-            bmass = DIM*DIM; /* recommended mass parameters for isotropic barostat */
-    }
-
-    snew(MassQ->QPinv, nnhpres*opts->nhchainlength);
-
-    /* barostat temperature */
-    if ((ir->tau_p > 0) && (opts->ref_t[0] > 0))
-    {
-        reft = max(0.0, opts->ref_t[0]);
-        kT   = BOLTZ*reft;
-        for (i = 0; i < nnhpres; i++)
-        {
-            for (j = 0; j < nh; j++)
-            {
-                if (j == 0)
-                {
-                    qmass = bmass;
-                }
-                else
-                {
-                    qmass = 1;
-                }
-                MassQ->QPinv[i*opts->nhchainlength+j]   = 1.0/(sqr(opts->tau_t[0]/M_2PI)*qmass*kT);
-            }
-        }
-    }
-    else
-    {
-        for (i = 0; i < nnhpres; i++)
-        {
-            for (j = 0; j < nh; j++)
-            {
-                MassQ->QPinv[i*nh+j] = 0.0;
-            }
-        }
-    }
-    return trotter_seq;
-}
-
-real NPT_energy(t_inputrec *ir, t_state *state, t_extmass *MassQ)
-{
-    int     i, j, bmass, qmass, ngtcall;
-    real    nd, ndj;
-    real    ener_npt, reft, eta, kT, tau;
-    double *ivxi, *ixi;
-    double *iQinv;
-    real    vol, dbaro, W, Q;
-    int     nh = state->nhchainlength;
-
-    ener_npt = 0;
-
-    /* now we compute the contribution of the pressure to the conserved quantity*/
-
-    if (ir->epc == epcMTTK)
-    {
-        /* find the volume, and the kinetic energy of the volume */
-
-        switch (ir->epct)
-        {
-
-            case epctISOTROPIC:
-                /* contribution from the pressure momenenta */
-                ener_npt += 0.5*sqr(state->veta)/MassQ->Winv;
-
-                /* contribution from the PV term */
-                vol       = det(state->box);
-                ener_npt += vol*trace(ir->ref_p)/(DIM*PRESFAC);
-
-                break;
-            case epctANISOTROPIC:
-
-                break;
-
-            case epctSURFACETENSION:
-
-                break;
-            case epctSEMIISOTROPIC:
-
-                break;
-            default:
-                break;
-        }
-    }
-
-    if (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir))
-    {
-        /* add the energy from the barostat thermostat chain */
-        for (i = 0; i < state->nnhpres; i++)
-        {
-
-            /* note -- assumes only one degree of freedom that is thermostatted in barostat */
-            ivxi  = &state->nhpres_vxi[i*nh];
-            ixi   = &state->nhpres_xi[i*nh];
-            iQinv = &(MassQ->QPinv[i*nh]);
-            reft  = max(ir->opts.ref_t[0], 0); /* using 'System' temperature */
-            kT    = BOLTZ * reft;
-
-            for (j = 0; j < nh; j++)
-            {
-                if (iQinv[j] > 0)
-                {
-                    ener_npt += 0.5*sqr(ivxi[j])/iQinv[j];
-                    /* contribution from the thermal variable of the NH chain */
-                    ener_npt += ixi[j]*kT;
-                }
-                if (debug)
-                {
-                    fprintf(debug, "P-T-group: %10d Chain %4d ThermV: %15.8f ThermX: %15.8f", i, j, ivxi[j], ixi[j]);
-                }
-            }
-        }
-    }
-
-    if (ir->etc)
-    {
-        for (i = 0; i < ir->opts.ngtc; i++)
-        {
-            ixi   = &state->nosehoover_xi[i*nh];
-            ivxi  = &state->nosehoover_vxi[i*nh];
-            iQinv = &(MassQ->Qinv[i*nh]);
-
-            nd   = ir->opts.nrdf[i];
-            reft = max(ir->opts.ref_t[i], 0);
-            kT   = BOLTZ * reft;
-
-            if (nd > 0.0)
-            {
-                if (IR_NVT_TROTTER(ir))
-                {
-                    /* contribution from the thermal momenta of the NH chain */
-                    for (j = 0; j < nh; j++)
-                    {
-                        if (iQinv[j] > 0)
-                        {
-                            ener_npt += 0.5*sqr(ivxi[j])/iQinv[j];
-                            /* contribution from the thermal variable of the NH chain */
-                            if (j == 0)
-                            {
-                                ndj = nd;
-                            }
-                            else
-                            {
-                                ndj = 1.0;
-                            }
-                            ener_npt += ndj*ixi[j]*kT;
-                        }
-                    }
-                }
-                else  /* Other non Trotter temperature NH control  -- no chains yet. */
-                {
-                    ener_npt += 0.5*BOLTZ*nd*sqr(ivxi[0])/iQinv[0];
-                    ener_npt += nd*ixi[0]*kT;
-                }
-            }
-        }
-    }
-    return ener_npt;
-}
-
-static real vrescale_gamdev(real ia,
-                            gmx_int64_t step, gmx_int64_t *count,
-                            gmx_int64_t seed1, gmx_int64_t seed2)
-/* Gamma distribution, adapted from numerical recipes */
-{
-    real   am, e, s, v1, v2, x, y;
-    double rnd[2];
-
-    assert(ia > 1);
-
-    do
-    {
-        do
-        {
-            do
-            {
-                gmx_rng_cycle_2uniform(step, (*count)++, seed1, seed2, rnd);
-                v1 = rnd[0];
-                v2 = 2.0*rnd[1] - 1.0;
-            }
-            while (v1*v1 + v2*v2 > 1.0 ||
-                   v1*v1*GMX_REAL_MAX < 3.0*ia);
-            /* The last check above ensures that both x (3.0 > 2.0 in s)
-             * and the pre-factor for e do not go out of range.
-             */
-            y  = v2/v1;
-            am = ia - 1;
-            s  = sqrt(2.0*am + 1.0);
-            x  = s*y + am;
-        }
-        while (x <= 0.0);
-
-        e = (1.0 + y*y)*exp(am*log(x/am) - s*y);
-
-        gmx_rng_cycle_2uniform(step, (*count)++, seed1, seed2, rnd);
-    }
-    while (rnd[0] > e);
-
-    return x;
-}
-
-static real gaussian_count(gmx_int64_t step, gmx_int64_t *count,
-                           gmx_int64_t seed1, gmx_int64_t seed2)
-{
-    double rnd[2], x, y, r;
-
-    do
-    {
-        gmx_rng_cycle_2uniform(step, (*count)++, seed1, seed2, rnd);
-        x = 2.0*rnd[0] - 1.0;
-        y = 2.0*rnd[1] - 1.0;
-        r = x*x + y*y;
-    }
-    while (r > 1.0 || r == 0.0);
-
-    r = sqrt(-2.0*log(r)/r);
-
-    return x*r;
-}
-
-static real vrescale_sumnoises(real nn,
-                               gmx_int64_t step, gmx_int64_t *count,
-                               gmx_int64_t seed1, gmx_int64_t seed2)
-{
-/*
- * Returns the sum of nn independent gaussian noises squared
- * (i.e. equivalent to summing the square of the return values
- * of nn calls to gmx_rng_gaussian_real).
- */
-    const real ndeg_tol = 0.0001;
-    real       r;
-
-    if (nn < 2 + ndeg_tol)
-    {
-        int  nn_int, i;
-        real gauss;
-
-        nn_int = (int)(nn + 0.5);
-
-        if (nn - nn_int < -ndeg_tol || nn - nn_int > ndeg_tol)
-        {
-            gmx_fatal(FARGS, "The v-rescale thermostat was called with a group with #DOF=%f, but for #DOF<3 only integer #DOF are supported", nn + 1);
-        }
-
-        r = 0;
-        for (i = 0; i < nn_int; i++)
-        {
-            gauss = gaussian_count(step, count, seed1, seed2);
-
-            r += gauss*gauss;
-        }
-    }
-    else
-    {
-        /* Use a gamma distribution for any real nn > 2 */
-        r = 2.0*vrescale_gamdev(0.5*nn, step, count, seed1, seed2);
-    }
-
-    return r;
-}
-
-static real vrescale_resamplekin(real kk, real sigma, real ndeg, real taut,
-                                 gmx_int64_t step, gmx_int64_t seed)
-{
-/*
- * Generates a new value for the kinetic energy,
- * according to Bussi et al JCP (2007), Eq. (A7)
- * kk:    present value of the kinetic energy of the atoms to be thermalized (in arbitrary units)
- * sigma: target average value of the kinetic energy (ndeg k_b T/2)  (in the same units as kk)
- * ndeg:  number of degrees of freedom of the atoms to be thermalized
- * taut:  relaxation time of the thermostat, in units of 'how often this routine is called'
- */
-    /* rnd_count tracks the step-local state for the cycle random
-     * number generator.
-     */
-    gmx_int64_t rnd_count = 0;
-    real        factor, rr, ekin_new;
-
-    if (taut > 0.1)
-    {
-        factor = exp(-1.0/taut);
-    }
-    else
-    {
-        factor = 0.0;
-    }
-
-    rr = gaussian_count(step, &rnd_count, seed, RND_SEED_VRESCALE);
-
-    ekin_new =
-        kk +
-        (1.0 - factor)*(sigma*(vrescale_sumnoises(ndeg-1, step, &rnd_count, seed, RND_SEED_VRESCALE) + rr*rr)/ndeg - kk) +
-        2.0*rr*sqrt(kk*sigma/ndeg*(1.0 - factor)*factor);
-
-    return ekin_new;
-}
-
-void vrescale_tcoupl(t_inputrec *ir, gmx_int64_t step,
-                     gmx_ekindata_t *ekind, real dt,
-                     double therm_integral[])
-{
-    t_grpopts *opts;
-    int        i;
-    real       Ek, Ek_ref1, Ek_ref, Ek_new;
-
-    opts = &ir->opts;
-
-    for (i = 0; (i < opts->ngtc); i++)
-    {
-        if (ir->eI == eiVV)
-        {
-            Ek = trace(ekind->tcstat[i].ekinf);
-        }
-        else
-        {
-            Ek = trace(ekind->tcstat[i].ekinh);
-        }
-
-        if (opts->tau_t[i] >= 0 && opts->nrdf[i] > 0 && Ek > 0)
-        {
-            Ek_ref1 = 0.5*opts->ref_t[i]*BOLTZ;
-            Ek_ref  = Ek_ref1*opts->nrdf[i];
-
-            Ek_new  = vrescale_resamplekin(Ek, Ek_ref, opts->nrdf[i],
-                                           opts->tau_t[i]/dt,
-                                           step, ir->ld_seed);
-
-            /* Analytically Ek_new>=0, but we check for rounding errors */
-            if (Ek_new <= 0)
-            {
-                ekind->tcstat[i].lambda = 0.0;
-            }
-            else
-            {
-                ekind->tcstat[i].lambda = sqrt(Ek_new/Ek);
-            }
-
-            therm_integral[i] -= Ek_new - Ek;
-
-            if (debug)
-            {
-                fprintf(debug, "TC: group %d: Ekr %g, Ek %g, Ek_new %g, Lambda: %g\n",
-                        i, Ek_ref, Ek, Ek_new, ekind->tcstat[i].lambda);
-            }
-        }
-        else
-        {
-            ekind->tcstat[i].lambda = 1.0;
-        }
-    }
-}
-
-real vrescale_energy(t_grpopts *opts, double therm_integral[])
-{
-    int  i;
-    real ener;
-
-    ener = 0;
-    for (i = 0; i < opts->ngtc; i++)
-    {
-        ener += therm_integral[i];
-    }
-
-    return ener;
-}
-
-void rescale_velocities(gmx_ekindata_t *ekind, t_mdatoms *mdatoms,
-                        int start, int end, rvec v[])
-{
-    t_grp_acc      *gstat;
-    t_grp_tcstat   *tcstat;
-    unsigned short *cACC, *cTC;
-    int             ga, gt, n, d;
-    real            lg;
-    rvec            vrel;
-
-    tcstat = ekind->tcstat;
-    cTC    = mdatoms->cTC;
-
-    if (ekind->bNEMD)
-    {
-        gstat  = ekind->grpstat;
-        cACC   = mdatoms->cACC;
-
-        ga = 0;
-        gt = 0;
-        for (n = start; n < end; n++)
-        {
-            if (cACC)
-            {
-                ga   = cACC[n];
-            }
-            if (cTC)
-            {
-                gt   = cTC[n];
-            }
-            /* Only scale the velocity component relative to the COM velocity */
-            rvec_sub(v[n], gstat[ga].u, vrel);
-            lg = tcstat[gt].lambda;
-            for (d = 0; d < DIM; d++)
-            {
-                v[n][d] = gstat[ga].u[d] + lg*vrel[d];
-            }
-        }
-    }
-    else
-    {
-        gt = 0;
-        for (n = start; n < end; n++)
-        {
-            if (cTC)
-            {
-                gt   = cTC[n];
-            }
-            lg = tcstat[gt].lambda;
-            for (d = 0; d < DIM; d++)
-            {
-                v[n][d] *= lg;
-            }
-        }
-    }
-}
-
-
-/* set target temperatures if we are annealing */
-void update_annealing_target_temp(t_grpopts *opts, real t)
-{
-    int  i, j, n, npoints;
-    real pert, thist = 0, x;
-
-    for (i = 0; i < opts->ngtc; i++)
-    {
-        npoints = opts->anneal_npoints[i];
-        switch (opts->annealing[i])
-        {
-            case eannNO:
-                continue;
-            case  eannPERIODIC:
-                /* calculate time modulo the period */
-                pert  = opts->anneal_time[i][npoints-1];
-                n     = t / pert;
-                thist = t - n*pert; /* modulo time */
-                /* Make sure rounding didn't get us outside the interval */
-                if (fabs(thist-pert) < GMX_REAL_EPS*100)
-                {
-                    thist = 0;
-                }
-                break;
-            case eannSINGLE:
-                thist = t;
-                break;
-            default:
-                gmx_fatal(FARGS, "Death horror in update_annealing_target_temp (i=%d/%d npoints=%d)", i, opts->ngtc, npoints);
-        }
-        /* We are doing annealing for this group if we got here,
-         * and we have the (relative) time as thist.
-         * calculate target temp */
-        j = 0;
-        while ((j < npoints-1) && (thist > (opts->anneal_time[i][j+1])))
-        {
-            j++;
-        }
-        if (j < npoints-1)
-        {
-            /* Found our position between points j and j+1.
-             * Interpolate: x is the amount from j+1, (1-x) from point j
-             * First treat possible jumps in temperature as a special case.
-             */
-            if ((opts->anneal_time[i][j+1]-opts->anneal_time[i][j]) < GMX_REAL_EPS*100)
-            {
-                opts->ref_t[i] = opts->anneal_temp[i][j+1];
-            }
-            else
-            {
-                x = ((thist-opts->anneal_time[i][j])/
-                     (opts->anneal_time[i][j+1]-opts->anneal_time[i][j]));
-                opts->ref_t[i] = x*opts->anneal_temp[i][j+1]+(1-x)*opts->anneal_temp[i][j];
-            }
-        }
-        else
-        {
-            opts->ref_t[i] = opts->anneal_temp[i][npoints-1];
-        }
-    }
-}