Merge branch release-4-6
authorMark Abraham <mark.j.abraham@gmail.com>
Sun, 23 Mar 2014 11:00:07 +0000 (12:00 +0100)
committerMark Abraham <mark.j.abraham@gmail.com>
Sun, 23 Mar 2014 11:01:06 +0000 (12:01 +0100)
Change-Id: Ie4431fb0002cd36799c8e664dcf4e096e66bc0ea

1  2 
src/gromacs/gmxpreprocess/readir.c
src/gromacs/legacyheaders/domdec.h
src/gromacs/mdlib/clincs.c
src/gromacs/mdlib/constr.c
src/gromacs/mdlib/domdec_con.c

index 5675b2fab01740caf2615bfad14419fd92ea47ad,0000000000000000000000000000000000000000..72da1996f061af4954a43daaa82be7163fde6be1
mode 100644,000000..100644
--- /dev/null
@@@ -1,4265 -1,0 +1,4277 @@@
 +/*
 + * 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.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <ctype.h>
 +#include <stdlib.h>
 +#include <limits.h>
 +#include "sysstuff.h"
 +#include "smalloc.h"
 +#include "typedefs.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "macros.h"
 +#include "index.h"
 +#include "symtab.h"
 +#include "string2.h"
 +#include "readinp.h"
 +#include "warninp.h"
 +#include "readir.h"
 +#include "toputil.h"
 +#include "index.h"
 +#include "network.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "mtop_util.h"
 +#include "chargegroup.h"
 +#include "inputrec.h"
 +#include "calc_verletbuf.h"
 +
 +#define MAXPTR 254
 +#define NOGID  255
 +
 +/* Resource parameters
 + * Do not change any of these until you read the instruction
 + * in readinp.h. Some cpp's do not take spaces after the backslash
 + * (like the c-shell), which will give you a very weird compiler
 + * message.
 + */
 +
 +typedef struct t_inputrec_strings
 +{
 +    char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN],
 +         acc[STRLEN], accgrps[STRLEN], freeze[STRLEN], frdim[STRLEN],
 +         energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN],
 +         couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN],
 +         wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN];
 +    char   fep_lambda[efptNR][STRLEN];
 +    char   lambda_weights[STRLEN];
 +    char **pull_grp;
 +    char **rot_grp;
 +    char   anneal[STRLEN], anneal_npoints[STRLEN],
 +           anneal_time[STRLEN], anneal_temp[STRLEN];
 +    char   QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN],
 +           bSH[STRLEN], CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN],
 +           SAoff[STRLEN], SAsteps[STRLEN], bTS[STRLEN], bOPT[STRLEN];
 +    char efield_x[STRLEN], efield_xt[STRLEN], efield_y[STRLEN],
 +         efield_yt[STRLEN], efield_z[STRLEN], efield_zt[STRLEN];
 +
 +} gmx_inputrec_strings;
 +
 +static gmx_inputrec_strings *is = NULL;
 +
 +void init_inputrec_strings()
 +{
 +    if (is)
 +    {
 +        gmx_incons("Attempted to call init_inputrec_strings before calling done_inputrec_strings. Only one inputrec (i.e. .mdp file) can be parsed at a time.");
 +    }
 +    snew(is, 1);
 +}
 +
 +void done_inputrec_strings()
 +{
 +    sfree(is);
 +    is = NULL;
 +}
 +
 +static char swapgrp[STRLEN], splitgrp0[STRLEN], splitgrp1[STRLEN], solgrp[STRLEN];
 +
 +enum {
 +    egrptpALL,         /* All particles have to be a member of a group.     */
 +    egrptpALL_GENREST, /* A rest group with name is generated for particles *
 +                        * that are not part of any group.                   */
 +    egrptpPART,        /* As egrptpALL_GENREST, but no name is generated    *
 +                        * for the rest group.                               */
 +    egrptpONE          /* Merge all selected groups into one group,         *
 +                        * make a rest group for the remaining particles.    */
 +};
 +
 +static const char *constraints[eshNR+1]    = {
 +    "none", "h-bonds", "all-bonds", "h-angles", "all-angles", NULL
 +};
 +
 +static const char *couple_lam[ecouplamNR+1]    = {
 +    "vdw-q", "vdw", "q", "none", NULL
 +};
 +
 +void init_ir(t_inputrec *ir, t_gromppopts *opts)
 +{
 +    snew(opts->include, STRLEN);
 +    snew(opts->define, STRLEN);
 +    snew(ir->fepvals, 1);
 +    snew(ir->expandedvals, 1);
 +    snew(ir->simtempvals, 1);
 +}
 +
 +static void GetSimTemps(int ntemps, t_simtemp *simtemp, double *temperature_lambdas)
 +{
 +
 +    int i;
 +
 +    for (i = 0; i < ntemps; i++)
 +    {
 +        /* simple linear scaling -- allows more control */
 +        if (simtemp->eSimTempScale == esimtempLINEAR)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*temperature_lambdas[i];
 +        }
 +        else if (simtemp->eSimTempScale == esimtempGEOMETRIC)  /* should give roughly equal acceptance for constant heat capacity . . . */
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low * pow(simtemp->simtemp_high/simtemp->simtemp_low, (1.0*i)/(ntemps-1));
 +        }
 +        else if (simtemp->eSimTempScale == esimtempEXPONENTIAL)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*((exp(temperature_lambdas[i])-1)/(exp(1.0)-1));
 +        }
 +        else
 +        {
 +            char errorstr[128];
 +            sprintf(errorstr, "eSimTempScale=%d not defined", simtemp->eSimTempScale);
 +            gmx_fatal(FARGS, errorstr);
 +        }
 +    }
 +}
 +
 +
 +
 +static void _low_check(gmx_bool b, char *s, warninp_t wi)
 +{
 +    if (b)
 +    {
 +        warning_error(wi, s);
 +    }
 +}
 +
 +static void check_nst(const char *desc_nst, int nst,
 +                      const char *desc_p, int *p,
 +                      warninp_t wi)
 +{
 +    char buf[STRLEN];
 +
 +    if (*p > 0 && *p % nst != 0)
 +    {
 +        /* Round up to the next multiple of nst */
 +        *p = ((*p)/nst + 1)*nst;
 +        sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n",
 +                desc_p, desc_nst, desc_p, *p);
 +        warning(wi, buf);
 +    }
 +}
 +
 +static gmx_bool ir_NVE(const t_inputrec *ir)
 +{
 +    return ((ir->eI == eiMD || EI_VV(ir->eI)) && ir->etc == etcNO);
 +}
 +
 +static int lcd(int n1, int n2)
 +{
 +    int d, i;
 +
 +    d = 1;
 +    for (i = 2; (i <= n1 && i <= n2); i++)
 +    {
 +        if (n1 % i == 0 && n2 % i == 0)
 +        {
 +            d = i;
 +        }
 +    }
 +
 +    return d;
 +}
 +
 +static void process_interaction_modifier(const t_inputrec *ir, int *eintmod)
 +{
 +    if (*eintmod == eintmodPOTSHIFT_VERLET)
 +    {
 +        if (ir->cutoff_scheme == ecutsVERLET)
 +        {
 +            *eintmod = eintmodPOTSHIFT;
 +        }
 +        else
 +        {
 +            *eintmod = eintmodNONE;
 +        }
 +    }
 +}
 +
 +void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts,
 +              warninp_t wi)
 +/* Check internal consistency */
 +{
 +    /* Strange macro: first one fills the err_buf, and then one can check
 +     * the condition, which will print the message and increase the error
 +     * counter.
 +     */
 +#define CHECK(b) _low_check(b, err_buf, wi)
 +    char        err_buf[256], warn_buf[STRLEN];
 +    int         i, j;
 +    int         ns_type  = 0;
 +    real        dt_coupl = 0;
 +    real        dt_pcoupl;
 +    int         nstcmin;
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    /* BASIC CUT-OFF STUFF */
 +    if (ir->rcoulomb < 0)
 +    {
 +        warning_error(wi, "rcoulomb should be >= 0");
 +    }
 +    if (ir->rvdw < 0)
 +    {
 +        warning_error(wi, "rvdw should be >= 0");
 +    }
 +    if (ir->rlist < 0 &&
 +        !(ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0))
 +    {
 +        warning_error(wi, "rlist should be >= 0");
 +    }
 +
 +    process_interaction_modifier(ir, &ir->coulomb_modifier);
 +    process_interaction_modifier(ir, &ir->vdw_modifier);
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        warning_note(wi,
 +                     "The group cutoff scheme is deprecated in Gromacs 5.0 and will be removed in a future "
 +                     "release when all interaction forms are supported for the verlet scheme. The verlet "
 +                     "scheme already scales better, and it is compatible with GPUs and other accelerators.");
 +
 +        /* BASIC CUT-OFF STUFF */
 +        if (ir->rlist == 0 ||
 +            !((ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > ir->rlist) ||
 +              (ir_vdw_might_be_zero_at_cutoff(ir)     && ir->rvdw     > ir->rlist)))
 +        {
 +            /* No switched potential and/or no twin-range:
 +             * we can set the long-range cut-off to the maximum of the other cut-offs.
 +             */
 +            ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
 +        }
 +        else if (ir->rlistlong < 0)
 +        {
 +            ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
 +            sprintf(warn_buf, "rlistlong was not set, setting it to %g (no buffer)",
 +                    ir->rlistlong);
 +            warning(wi, warn_buf);
 +        }
 +        if (ir->rlistlong == 0 && ir->ePBC != epbcNONE)
 +        {
 +            warning_error(wi, "Can not have an infinite cut-off with PBC");
 +        }
 +        if (ir->rlistlong > 0 && (ir->rlist == 0 || ir->rlistlong < ir->rlist))
 +        {
 +            warning_error(wi, "rlistlong can not be shorter than rlist");
 +        }
 +        if (IR_TWINRANGE(*ir) && ir->nstlist <= 0)
 +        {
 +            warning_error(wi, "Can not have nstlist<=0 with twin-range interactions");
 +        }
 +    }
 +
 +    if (ir->rlistlong == ir->rlist)
 +    {
 +        ir->nstcalclr = 0;
 +    }
 +    else if (ir->rlistlong > ir->rlist && ir->nstcalclr == 0)
 +    {
 +        warning_error(wi, "With different cutoffs for electrostatics and VdW, nstcalclr must be -1 or a positive number");
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        real rc_max;
 +
 +        /* Normal Verlet type neighbor-list, currently only limited feature support */
 +        if (inputrec2nboundeddim(ir) < 3)
 +        {
 +            warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported");
 +        }
 +        if (ir->rcoulomb != ir->rvdw)
 +        {
 +            warning_error(wi, "With Verlet lists rcoulomb!=rvdw is not supported");
 +        }
 +        if (ir->vdwtype == evdwSHIFT || ir->vdwtype == evdwSWITCH)
 +        {
 +            if (ir->vdw_modifier == eintmodNONE ||
 +                ir->vdw_modifier == eintmodPOTSHIFT)
 +            {
 +                ir->vdw_modifier = (ir->vdwtype == evdwSHIFT ? eintmodFORCESWITCH : eintmodPOTSWITCH);
 +
 +                sprintf(warn_buf, "Replacing vdwtype=%s by the equivalent combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], evdw_names[evdwCUT], eintmod_names[ir->vdw_modifier]);
 +                warning_note(wi, warn_buf);
 +
 +                ir->vdwtype = evdwCUT;
 +            }
 +            else
 +            {
 +                sprintf(warn_buf, "Unsupported combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], eintmod_names[ir->vdw_modifier]);
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +
 +        if (!(ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME))
 +        {
 +            warning_error(wi, "With Verlet lists only cut-off and PME LJ interactions are supported");
 +        }
 +        if (!(ir->coulombtype == eelCUT ||
 +              (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC) ||
 +              EEL_PME(ir->coulombtype) || ir->coulombtype == eelEWALD))
 +        {
 +            warning_error(wi, "With Verlet lists only cut-off, reaction-field, PME and Ewald electrostatics are supported");
 +        }
 +        if (!(ir->coulomb_modifier == eintmodNONE ||
 +              ir->coulomb_modifier == eintmodPOTSHIFT))
 +        {
 +            sprintf(warn_buf, "coulomb_modifier=%s is not supported with the Verlet cut-off scheme", eintmod_names[ir->coulomb_modifier]);
 +            warning_error(wi, warn_buf);
 +        }
 +
 +        if (ir->nstlist <= 0)
 +        {
 +            warning_error(wi, "With Verlet lists nstlist should be larger than 0");
 +        }
 +
 +        if (ir->nstlist < 10)
 +        {
 +            warning_note(wi, "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note that with the Verlet scheme, nstlist has no effect on the accuracy of your simulation.");
 +        }
 +
 +        rc_max = max(ir->rvdw, ir->rcoulomb);
 +
 +        if (ir->verletbuf_tol <= 0)
 +        {
 +            if (ir->verletbuf_tol == 0)
 +            {
 +                warning_error(wi, "Can not have Verlet buffer tolerance of exactly 0");
 +            }
 +
 +            if (ir->rlist < rc_max)
 +            {
 +                warning_error(wi, "With verlet lists rlist can not be smaller than rvdw or rcoulomb");
 +            }
 +
 +            if (ir->rlist == rc_max && ir->nstlist > 1)
 +            {
 +                warning_note(wi, "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet buffer. The cluster pair list does have a buffering effect, but choosing a larger rlist might be necessary for good energy conservation.");
 +            }
 +        }
 +        else
 +        {
 +            if (ir->rlist > rc_max)
 +            {
 +                warning_note(wi, "You have set rlist larger than the interaction cut-off, but you also have verlet-buffer-tolerance > 0. Will set rlist using verlet-buffer-tolerance.");
 +            }
 +
 +            if (ir->nstlist == 1)
 +            {
 +                /* No buffer required */
 +                ir->rlist = rc_max;
 +            }
 +            else
 +            {
 +                if (EI_DYNAMICS(ir->eI))
 +                {
 +                    if (inputrec2nboundeddim(ir) < 3)
 +                    {
 +                        warning_error(wi, "The box volume is required for calculating rlist from the energy drift with verlet-buffer-tolerance > 0. You are using at least one unbounded dimension, so no volume can be computed. Either use a finite box, or set rlist yourself together with verlet-buffer-tolerance = -1.");
 +                    }
 +                    /* Set rlist temporarily so we can continue processing */
 +                    ir->rlist = rc_max;
 +                }
 +                else
 +                {
 +                    /* Set the buffer to 5% of the cut-off */
 +                    ir->rlist = (1.0 + verlet_buffer_ratio_nodynamics)*rc_max;
 +                }
 +            }
 +        }
 +
 +        /* No twin-range calculations with Verlet lists */
 +        ir->rlistlong = ir->rlist;
 +    }
 +
 +    if (ir->nstcalclr == -1)
 +    {
 +        /* if rlist=rlistlong, this will later be changed to nstcalclr=0 */
 +        ir->nstcalclr = ir->nstlist;
 +    }
 +    else if (ir->nstcalclr > 0)
 +    {
 +        if (ir->nstlist > 0 && (ir->nstlist % ir->nstcalclr != 0))
 +        {
 +            warning_error(wi, "nstlist must be evenly divisible by nstcalclr. Use nstcalclr = -1 to automatically follow nstlist");
 +        }
 +    }
 +    else if (ir->nstcalclr < -1)
 +    {
 +        warning_error(wi, "nstcalclr must be a positive number (divisor of nstcalclr), or -1 to follow nstlist.");
 +    }
 +
 +    if (EEL_PME(ir->coulombtype) && ir->rcoulomb > ir->rvdw && ir->nstcalclr > 1)
 +    {
 +        warning_error(wi, "When used with PME, the long-range component of twin-range interactions must be updated every step (nstcalclr)");
 +    }
 +
 +    /* GENERAL INTEGRATOR STUFF */
 +    if (!(ir->eI == eiMD || EI_VV(ir->eI)))
 +    {
 +        ir->etc = etcNO;
 +    }
 +    if (ir->eI == eiVVAK)
 +    {
 +        sprintf(warn_buf, "Integrator method %s is implemented primarily for validation purposes; for molecular dynamics, you should probably be using %s or %s", ei_names[eiVVAK], ei_names[eiMD], ei_names[eiVV]);
 +        warning_note(wi, warn_buf);
 +    }
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        ir->epc = epcNO;
 +    }
 +    if (EI_DYNAMICS(ir->eI))
 +    {
 +        if (ir->nstcalcenergy < 0)
 +        {
 +            ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir);
 +            if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy)
 +            {
 +                /* nstcalcenergy larger than nstener does not make sense.
 +                 * We ideally want nstcalcenergy=nstener.
 +                 */
 +                if (ir->nstlist > 0)
 +                {
 +                    ir->nstcalcenergy = lcd(ir->nstenergy, ir->nstlist);
 +                }
 +                else
 +                {
 +                    ir->nstcalcenergy = ir->nstenergy;
 +                }
 +            }
 +        }
 +        else if ( (ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy) ||
 +                  (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 &&
 +                   (ir->nstcalcenergy > ir->fepvals->nstdhdl) ) )
 +
 +        {
 +            const char *nsten    = "nstenergy";
 +            const char *nstdh    = "nstdhdl";
 +            const char *min_name = nsten;
 +            int         min_nst  = ir->nstenergy;
 +
 +            /* find the smallest of ( nstenergy, nstdhdl ) */
 +            if (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 &&
 +                (ir->nstenergy == 0 || ir->fepvals->nstdhdl < ir->nstenergy))
 +            {
 +                min_nst  = ir->fepvals->nstdhdl;
 +                min_name = nstdh;
 +            }
 +            /* If the user sets nstenergy small, we should respect that */
 +            sprintf(warn_buf,
 +                    "Setting nstcalcenergy (%d) equal to %s (%d)",
 +                    ir->nstcalcenergy, min_name, min_nst);
 +            warning_note(wi, warn_buf);
 +            ir->nstcalcenergy = min_nst;
 +        }
 +
 +        if (ir->epc != epcNO)
 +        {
 +            if (ir->nstpcouple < 0)
 +            {
 +                ir->nstpcouple = ir_optimal_nstpcouple(ir);
 +            }
 +        }
 +        if (IR_TWINRANGE(*ir))
 +        {
 +            check_nst("nstlist", ir->nstlist,
 +                      "nstcalcenergy", &ir->nstcalcenergy, wi);
 +            if (ir->epc != epcNO)
 +            {
 +                check_nst("nstlist", ir->nstlist,
 +                          "nstpcouple", &ir->nstpcouple, wi);
 +            }
 +        }
 +
 +        if (ir->nstcalcenergy > 0)
 +        {
 +            if (ir->efep != efepNO)
 +            {
 +                /* nstdhdl should be a multiple of nstcalcenergy */
 +                check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                          "nstdhdl", &ir->fepvals->nstdhdl, wi);
 +                /* nstexpanded should be a multiple of nstcalcenergy */
 +                check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                          "nstexpanded", &ir->expandedvals->nstexpanded, wi);
 +            }
 +            /* for storing exact averages nstenergy should be
 +             * a multiple of nstcalcenergy
 +             */
 +            check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                      "nstenergy", &ir->nstenergy, wi);
 +        }
 +    }
 +
 +    /* LD STUFF */
 +    if ((EI_SD(ir->eI) || ir->eI == eiBD) &&
 +        ir->bContinuation && ir->ld_seed != -1)
 +    {
 +        warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)");
 +    }
 +
 +    /* TPI STUFF */
 +    if (EI_TPI(ir->eI))
 +    {
 +        sprintf(err_buf, "TPI only works with pbc = %s", epbc_names[epbcXYZ]);
 +        CHECK(ir->ePBC != epbcXYZ);
 +        sprintf(err_buf, "TPI only works with ns = %s", ens_names[ensGRID]);
 +        CHECK(ir->ns_type != ensGRID);
 +        sprintf(err_buf, "with TPI nstlist should be larger than zero");
 +        CHECK(ir->nstlist <= 0);
 +        sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
 +        CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype));
 +    }
 +
 +    /* SHAKE / LINCS */
 +    if ( (opts->nshake > 0) && (opts->bMorse) )
 +    {
 +        sprintf(warn_buf,
 +                "Using morse bond-potentials while constraining bonds is useless");
 +        warning(wi, warn_buf);
 +    }
 +
 +    if ((EI_SD(ir->eI) || ir->eI == eiBD) &&
 +        ir->bContinuation && ir->ld_seed != -1)
 +    {
 +        warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)");
 +    }
 +    /* verify simulated tempering options */
 +
 +    if (ir->bSimTemp)
 +    {
 +        gmx_bool bAllTempZero = TRUE;
 +        for (i = 0; i < fep->n_lambda; i++)
 +        {
 +            sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[efptTEMPERATURE], fep->all_lambda[efptTEMPERATURE][i]);
 +            CHECK((fep->all_lambda[efptTEMPERATURE][i] < 0) || (fep->all_lambda[efptTEMPERATURE][i] > 1));
 +            if (fep->all_lambda[efptTEMPERATURE][i] > 0)
 +            {
 +                bAllTempZero = FALSE;
 +            }
 +        }
 +        sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero");
 +        CHECK(bAllTempZero == TRUE);
 +
 +        sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv");
 +        CHECK(ir->eI != eiVV);
 +
 +        /* check compatability of the temperature coupling with simulated tempering */
 +
 +        if (ir->etc == etcNOSEHOOVER)
 +        {
 +            sprintf(warn_buf, "Nose-Hoover based temperature control such as [%s] my not be entirelyconsistent with simulated tempering", etcoupl_names[ir->etc]);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        /* check that the temperatures make sense */
 +
 +        sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= than the simulated tempering lower temperature (%g)", ir->simtempvals->simtemp_high, ir->simtempvals->simtemp_low);
 +        CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low);
 +
 +        sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_high);
 +        CHECK(ir->simtempvals->simtemp_high <= 0);
 +
 +        sprintf(err_buf, "Lower simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_low);
 +        CHECK(ir->simtempvals->simtemp_low <= 0);
 +    }
 +
 +    /* verify free energy options */
 +
 +    if (ir->efep != efepNO)
 +    {
 +        fep = ir->fepvals;
 +        sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2",
 +                fep->sc_power);
 +        CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2);
 +
 +        sprintf(err_buf, "The soft-core sc-r-power is %d and can only be 6 or 48",
 +                (int)fep->sc_r_power);
 +        CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0 && fep->sc_r_power != 48.0);
 +
 +        sprintf(err_buf, "Can't use postive delta-lambda (%g) if initial state/lambda does not start at zero", fep->delta_lambda);
 +        CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) ||  (fep->init_lambda > 0)));
 +
 +        sprintf(err_buf, "Can't use postive delta-lambda (%g) with expanded ensemble simulations", fep->delta_lambda);
 +        CHECK(fep->delta_lambda > 0 && (ir->efep == efepEXPANDED));
 +
 +        sprintf(err_buf, "Can only use expanded ensemble with md-vv for now; should be supported for other integrators in 5.0");
 +        CHECK(!(EI_VV(ir->eI)) && (ir->efep == efepEXPANDED));
 +
 +        sprintf(err_buf, "Free-energy not implemented for Ewald");
 +        CHECK(ir->coulombtype == eelEWALD);
 +
 +        /* check validty of lambda inputs */
 +        if (fep->n_lambda == 0)
 +        {
 +            /* Clear output in case of no states:*/
 +            sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state);
 +            CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0));
 +        }
 +        else
 +        {
 +            sprintf(err_buf, "initial thermodynamic state %d does not exist, only goes to %d", fep->init_fep_state, fep->n_lambda-1);
 +            CHECK((fep->init_fep_state >= fep->n_lambda));
 +        }
 +
 +        sprintf(err_buf, "Lambda state must be set, either with init-lambda-state or with init-lambda");
 +        CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0));
 +
 +        sprintf(err_buf, "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with init-lambda-state or with init-lambda, but not both",
 +                fep->init_lambda, fep->init_fep_state);
 +        CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0));
 +
 +
 +
 +        if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0))
 +        {
 +            int n_lambda_terms;
 +            n_lambda_terms = 0;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                if (fep->separate_dvdl[i])
 +                {
 +                    n_lambda_terms++;
 +                }
 +            }
 +            if (n_lambda_terms > 1)
 +            {
 +                sprintf(warn_buf, "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't use init-lambda to set lambda state (except for slow growth). Use init-lambda-state instead.");
 +                warning(wi, warn_buf);
 +            }
 +
 +            if (n_lambda_terms < 2 && fep->n_lambda > 0)
 +            {
 +                warning_note(wi,
 +                             "init-lambda is deprecated for setting lambda state (except for slow growth). Use init-lambda-state instead.");
 +            }
 +        }
 +
 +        for (j = 0; j < efptNR; j++)
 +        {
 +            for (i = 0; i < fep->n_lambda; i++)
 +            {
 +                sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[j], fep->all_lambda[j][i]);
 +                CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1));
 +            }
 +        }
 +
 +        if ((fep->sc_alpha > 0) && (!fep->bScCoul))
 +        {
 +            for (i = 0; i < fep->n_lambda; i++)
 +            {
 +                sprintf(err_buf, "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to crashes, and is not supported.", i, fep->all_lambda[efptVDW][i],
 +                        fep->all_lambda[efptCOUL][i]);
 +                CHECK((fep->sc_alpha > 0) &&
 +                      (((fep->all_lambda[efptCOUL][i] > 0.0) &&
 +                        (fep->all_lambda[efptCOUL][i] < 1.0)) &&
 +                       ((fep->all_lambda[efptVDW][i] > 0.0) &&
 +                        (fep->all_lambda[efptVDW][i] < 1.0))));
 +            }
 +        }
 +
 +        if ((fep->bScCoul) && (EEL_PME(ir->coulombtype)))
 +        {
 +            real sigma, lambda, r_sc;
 +
 +            sigma  = 0.34;
 +            /* Maximum estimate for A and B charges equal with lambda power 1 */
 +            lambda = 0.5;
 +            r_sc   = pow(lambda*fep->sc_alpha*pow(sigma/ir->rcoulomb, fep->sc_r_power) + 1.0, 1.0/fep->sc_r_power);
 +            sprintf(warn_buf, "With PME there is a minor soft core effect present at the cut-off, proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on energy conservation, but usually other effects dominate. With a common sigma value of %g nm the fraction of the particle-particle potential at the cut-off at lambda=%g is around %.1e, while ewald-rtol is %.1e.",
 +                    fep->sc_r_power,
 +                    sigma, lambda, r_sc - 1.0, ir->ewald_rtol);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        /*  Free Energy Checks -- In an ideal world, slow growth and FEP would
 +            be treated differently, but that's the next step */
 +
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                sprintf(err_buf, "%s[%d] must be between 0 and 1", efpt_names[i], j);
 +                CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1));
 +            }
 +        }
 +    }
 +
 +    if ((ir->bSimTemp) || (ir->efep == efepEXPANDED))
 +    {
 +        fep    = ir->fepvals;
 +        expand = ir->expandedvals;
 +
 +        /* checking equilibration of weights inputs for validity */
 +
 +        sprintf(err_buf, "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]);
 +        CHECK((expand->equil_n_at_lam > 0) && (expand->elmceq != elmceqNUMATLAM));
 +
 +        sprintf(err_buf, "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_samples, elmceq_names[elmceqSAMPLES]);
 +        CHECK((expand->equil_samples > 0) && (expand->elmceq != elmceqSAMPLES));
 +
 +        sprintf(err_buf, "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_steps, elmceq_names[elmceqSTEPS]);
 +        CHECK((expand->equil_steps > 0) && (expand->elmceq != elmceqSTEPS));
 +
 +        sprintf(err_buf, "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_samples, elmceq_names[elmceqWLDELTA]);
 +        CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != elmceqWLDELTA));
 +
 +        sprintf(err_buf, "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_ratio, elmceq_names[elmceqRATIO]);
 +        CHECK((expand->equil_ratio > 0) && (expand->elmceq != elmceqRATIO));
 +
 +        sprintf(err_buf, "weight-equil-number-all-lambda (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]);
 +        CHECK((expand->equil_n_at_lam <= 0) && (expand->elmceq == elmceqNUMATLAM));
 +
 +        sprintf(err_buf, "weight-equil-number-samples (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_samples, elmceq_names[elmceqSAMPLES]);
 +        CHECK((expand->equil_samples <= 0) && (expand->elmceq == elmceqSAMPLES));
 +
 +        sprintf(err_buf, "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_steps, elmceq_names[elmceqSTEPS]);
 +        CHECK((expand->equil_steps <= 0) && (expand->elmceq == elmceqSTEPS));
 +
 +        sprintf(err_buf, "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s",
 +                expand->equil_wl_delta, elmceq_names[elmceqWLDELTA]);
 +        CHECK((expand->equil_wl_delta <= 0) && (expand->elmceq == elmceqWLDELTA));
 +
 +        sprintf(err_buf, "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s",
 +                expand->equil_ratio, elmceq_names[elmceqRATIO]);
 +        CHECK((expand->equil_ratio <= 0) && (expand->elmceq == elmceqRATIO));
 +
 +        sprintf(err_buf, "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s",
 +                elmceq_names[elmceqWLDELTA], elamstats_names[elamstatsWL], elamstats_names[elamstatsWWL]);
 +        CHECK((expand->elmceq == elmceqWLDELTA) && (!EWL(expand->elamstats)));
 +
 +        sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats);
 +        CHECK((expand->lmc_repeats <= 0));
 +        sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin);
 +        CHECK((expand->minvarmin <= 0));
 +        sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range);
 +        CHECK((expand->c_range < 0));
 +        sprintf(err_buf, "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != 'no'",
 +                fep->init_fep_state, expand->lmc_forced_nstart);
 +        CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0) && (expand->elmcmove != elmcmoveNO));
 +        sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart);
 +        CHECK((expand->lmc_forced_nstart < 0));
 +        sprintf(err_buf, "init-lambda-state (%d) must be in the interval [0,number of lambdas)", fep->init_fep_state);
 +        CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda));
 +
 +        sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta);
 +        CHECK((expand->init_wl_delta < 0));
 +        sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio);
 +        CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1));
 +        sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale);
 +        CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1));
 +
 +        /* if there is no temperature control, we need to specify an MC temperature */
 +        sprintf(err_buf, "If there is no temperature control, and lmc-mcmove!= 'no',mc_temperature must be set to a positive number");
 +        if (expand->nstTij > 0)
 +        {
 +            sprintf(err_buf, "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)",
 +                    expand->nstTij, ir->nstlog);
 +            CHECK((mod(expand->nstTij, ir->nstlog) != 0));
 +        }
 +    }
 +
 +    /* PBC/WALLS */
 +    sprintf(err_buf, "walls only work with pbc=%s", epbc_names[epbcXY]);
 +    CHECK(ir->nwall && ir->ePBC != epbcXY);
 +
 +    /* VACUUM STUFF */
 +    if (ir->ePBC != epbcXYZ && ir->nwall != 2)
 +    {
 +        if (ir->ePBC == epbcNONE)
 +        {
 +            if (ir->epc != epcNO)
 +            {
 +                warning(wi, "Turning off pressure coupling for vacuum system");
 +                ir->epc = epcNO;
 +            }
 +        }
 +        else
 +        {
 +            sprintf(err_buf, "Can not have pressure coupling with pbc=%s",
 +                    epbc_names[ir->ePBC]);
 +            CHECK(ir->epc != epcNO);
 +        }
 +        sprintf(err_buf, "Can not have Ewald with pbc=%s", epbc_names[ir->ePBC]);
 +        CHECK(EEL_FULL(ir->coulombtype));
 +
 +        sprintf(err_buf, "Can not have dispersion correction with pbc=%s",
 +                epbc_names[ir->ePBC]);
 +        CHECK(ir->eDispCorr != edispcNO);
 +    }
 +
 +    if (ir->rlist == 0.0)
 +    {
 +        sprintf(err_buf, "can only have neighborlist cut-off zero (=infinite)\n"
 +                "with coulombtype = %s or coulombtype = %s\n"
 +                "without periodic boundary conditions (pbc = %s) and\n"
 +                "rcoulomb and rvdw set to zero",
 +                eel_names[eelCUT], eel_names[eelUSER], epbc_names[epbcNONE]);
 +        CHECK(((ir->coulombtype != eelCUT) && (ir->coulombtype != eelUSER)) ||
 +              (ir->ePBC     != epbcNONE) ||
 +              (ir->rcoulomb != 0.0)      || (ir->rvdw != 0.0));
 +
 +        if (ir->nstlist < 0)
 +        {
 +            warning_error(wi, "Can not have heuristic neighborlist updates without cut-off");
 +        }
 +        if (ir->nstlist > 0)
 +        {
 +            warning_note(wi, "Simulating without cut-offs can be (slightly) faster with nstlist=0, nstype=simple and only one MPI rank");
 +        }
 +    }
 +
 +    /* COMM STUFF */
 +    if (ir->nstcomm == 0)
 +    {
 +        ir->comm_mode = ecmNO;
 +    }
 +    if (ir->comm_mode != ecmNO)
 +    {
 +        if (ir->nstcomm < 0)
 +        {
 +            warning(wi, "If you want to remove the rotation around the center of mass, you should set comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to its absolute value");
 +            ir->nstcomm = abs(ir->nstcomm);
 +        }
 +
 +        if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy)
 +        {
 +            warning_note(wi, "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting nstcomm to nstcalcenergy");
 +            ir->nstcomm = ir->nstcalcenergy;
 +        }
 +
 +        if (ir->comm_mode == ecmANGULAR)
 +        {
 +            sprintf(err_buf, "Can not remove the rotation around the center of mass with periodic molecules");
 +            CHECK(ir->bPeriodicMols);
 +            if (ir->ePBC != epbcNONE)
 +            {
 +                warning(wi, "Removing the rotation around the center of mass in a periodic system (this is not a problem when you have only one molecule).");
 +            }
 +        }
 +    }
 +
 +    if (EI_STATE_VELOCITY(ir->eI) && ir->ePBC == epbcNONE && ir->comm_mode != ecmANGULAR)
 +    {
 +        warning_note(wi, "Tumbling and or flying ice-cubes: We are not removing rotation around center of mass in a non-periodic system. You should probably set comm_mode = ANGULAR.");
 +    }
 +
 +    sprintf(err_buf, "Twin-range neighbour searching (NS) with simple NS"
 +            " algorithm not implemented");
 +    CHECK(((ir->rcoulomb > ir->rlist) || (ir->rvdw > ir->rlist))
 +          && (ir->ns_type == ensSIMPLE));
 +
 +    /* TEMPERATURE COUPLING */
 +    if (ir->etc == etcYES)
 +    {
 +        ir->etc = etcBERENDSEN;
 +        warning_note(wi, "Old option for temperature coupling given: "
 +                     "changing \"yes\" to \"Berendsen\"\n");
 +    }
 +
 +    if ((ir->etc == etcNOSEHOOVER) || (ir->epc == epcMTTK))
 +    {
 +        if (ir->opts.nhchainlength < 1)
 +        {
 +            sprintf(warn_buf, "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to 1\n", ir->opts.nhchainlength);
 +            ir->opts.nhchainlength = 1;
 +            warning(wi, warn_buf);
 +        }
 +
 +        if (ir->etc == etcNOSEHOOVER && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1)
 +        {
 +            warning_note(wi, "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1");
 +            ir->opts.nhchainlength = 1;
 +        }
 +    }
 +    else
 +    {
 +        ir->opts.nhchainlength = 0;
 +    }
 +
 +    if (ir->eI == eiVVAK)
 +    {
 +        sprintf(err_buf, "%s implemented primarily for validation, and requires nsttcouple = 1 and nstpcouple = 1.",
 +                ei_names[eiVVAK]);
 +        CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1));
 +    }
 +
 +    if (ETC_ANDERSEN(ir->etc))
 +    {
 +        sprintf(err_buf, "%s temperature control not supported for integrator %s.", etcoupl_names[ir->etc], ei_names[ir->eI]);
 +        CHECK(!(EI_VV(ir->eI)));
 +
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            sprintf(err_buf, "all tau_t must currently be equal using Andersen temperature control, violated for group %d", i);
 +            CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]);
 +            sprintf(err_buf, "all tau_t must be postive using Andersen temperature control, tau_t[%d]=%10.6f",
 +                    i, ir->opts.tau_t[i]);
 +            CHECK(ir->opts.tau_t[i] < 0);
 +        }
 +        if (ir->nstcomm > 0 && (ir->etc == etcANDERSEN))
 +        {
 +            sprintf(warn_buf, "Center of mass removal not necessary for %s.  All velocities of coupled groups are rerandomized periodically, so flying ice cube errors will not occur.", etcoupl_names[ir->etc]);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        sprintf(err_buf, "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are randomized every time step", ir->nstcomm, etcoupl_names[ir->etc]);
 +        CHECK(ir->nstcomm > 1 && (ir->etc == etcANDERSEN));
 +
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            int nsteps = (int)(ir->opts.tau_t[i]/ir->delta_t);
 +            sprintf(err_buf, "tau_t/delta_t for group %d for temperature control method %s must be a multiple of nstcomm (%d), as velocities of atoms in coupled groups are randomized every time step. The input tau_t (%8.3f) leads to %d steps per randomization", i, etcoupl_names[ir->etc], ir->nstcomm, ir->opts.tau_t[i], nsteps);
 +            CHECK((nsteps % ir->nstcomm) && (ir->etc == etcANDERSENMASSIVE));
 +        }
 +    }
 +    if (ir->etc == etcBERENDSEN)
 +    {
 +        sprintf(warn_buf, "The %s thermostat does not generate the correct kinetic energy distribution. You might want to consider using the %s thermostat.",
 +                ETCOUPLTYPE(ir->etc), ETCOUPLTYPE(etcVRESCALE));
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if ((ir->etc == etcNOSEHOOVER || ETC_ANDERSEN(ir->etc))
 +        && ir->epc == epcBERENDSEN)
 +    {
 +        sprintf(warn_buf, "Using Berendsen pressure coupling invalidates the "
 +                "true ensemble for the thermostat");
 +        warning(wi, warn_buf);
 +    }
 +
 +    /* PRESSURE COUPLING */
 +    if (ir->epc == epcISOTROPIC)
 +    {
 +        ir->epc = epcBERENDSEN;
 +        warning_note(wi, "Old option for pressure coupling given: "
 +                     "changing \"Isotropic\" to \"Berendsen\"\n");
 +    }
 +
 +    if (ir->epc != epcNO)
 +    {
 +        dt_pcoupl = ir->nstpcouple*ir->delta_t;
 +
 +        sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p);
 +        CHECK(ir->tau_p <= 0);
 +
 +        if (ir->tau_p/dt_pcoupl < pcouple_min_integration_steps(ir->epc))
 +        {
 +            sprintf(warn_buf, "For proper integration of the %s barostat, tau-p (%g) should be at least %d times larger than nstpcouple*dt (%g)",
 +                    EPCOUPLTYPE(ir->epc), ir->tau_p, pcouple_min_integration_steps(ir->epc), dt_pcoupl);
 +            warning(wi, warn_buf);
 +        }
 +
 +        sprintf(err_buf, "compressibility must be > 0 when using pressure"
 +                " coupling %s\n", EPCOUPLTYPE(ir->epc));
 +        CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 ||
 +              ir->compress[ZZ][ZZ] < 0 ||
 +              (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 &&
 +               ir->compress[ZZ][XX] <= 0 && ir->compress[ZZ][YY] <= 0));
 +
 +        if (epcPARRINELLORAHMAN == ir->epc && opts->bGenVel)
 +        {
 +            sprintf(warn_buf,
 +                    "You are generating velocities so I am assuming you "
 +                    "are equilibrating a system. You are using "
 +                    "%s pressure coupling, but this can be "
 +                    "unstable for equilibration. If your system crashes, try "
 +                    "equilibrating first with Berendsen pressure coupling. If "
 +                    "you are not equilibrating the system, you can probably "
 +                    "ignore this warning.",
 +                    epcoupl_names[ir->epc]);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EI_VV(ir->eI))
 +    {
 +        if (ir->epc > epcNO)
 +        {
 +            if ((ir->epc != epcBERENDSEN) && (ir->epc != epcMTTK))
 +            {
 +                warning_error(wi, "for md-vv and md-vv-avek, can only use Berendsen and Martyna-Tuckerman-Tobias-Klein (MTTK) equations for pressure control; MTTK is equivalent to Parrinello-Rahman.");
 +            }
 +        }
 +    }
++    else
++    {
++        if (ir->epc == epcMTTK)
++        {
++            warning_error(wi, "MTTK pressure coupling requires a Velocity-verlet integrator");
++        }
++    }
 +
 +    /* ELECTROSTATICS */
 +    /* More checks are in triple check (grompp.c) */
 +
 +    if (ir->coulombtype == eelSWITCH)
 +    {
 +        sprintf(warn_buf, "coulombtype = %s is only for testing purposes and can lead to serious "
 +                "artifacts, advice: use coulombtype = %s",
 +                eel_names[ir->coulombtype],
 +                eel_names[eelRF_ZERO]);
 +        warning(wi, warn_buf);
 +    }
 +
 +    if (ir->epsilon_r != 1 && ir->implicit_solvent == eisGBSA)
 +    {
 +        sprintf(warn_buf, "epsilon-r = %g with GB implicit solvent, will use this value for inner dielectric", ir->epsilon_r);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1)
 +    {
 +        sprintf(warn_buf, "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old format and exchanging epsilon-r and epsilon-rf", ir->epsilon_r);
 +        warning(wi, warn_buf);
 +        ir->epsilon_rf = ir->epsilon_r;
 +        ir->epsilon_r  = 1.0;
 +    }
 +
 +    if (getenv("GALACTIC_DYNAMICS") == NULL)
 +    {
 +        sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r);
 +        CHECK(ir->epsilon_r < 0);
 +    }
 +
 +    if (EEL_RF(ir->coulombtype))
 +    {
 +        /* reaction field (at the cut-off) */
 +
 +        if (ir->coulombtype == eelRF_ZERO)
 +        {
 +            sprintf(warn_buf, "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->epsilon_rf != 0);
 +            ir->epsilon_rf = 0.0;
 +        }
 +
 +        sprintf(err_buf, "epsilon-rf must be >= epsilon-r");
 +        CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) ||
 +              (ir->epsilon_r == 0));
 +        if (ir->epsilon_rf == ir->epsilon_r)
 +        {
 +            sprintf(warn_buf, "Using epsilon-rf = epsilon-r with %s does not make sense",
 +                    eel_names[ir->coulombtype]);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +    /* Allow rlist>rcoulomb for tabulated long range stuff. This just
 +     * means the interaction is zero outside rcoulomb, but it helps to
 +     * provide accurate energy conservation.
 +     */
 +    if (ir_coulomb_might_be_zero_at_cutoff(ir))
 +    {
 +        if (ir_coulomb_switched(ir))
 +        {
 +            sprintf(err_buf,
 +                    "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the potential modifier options!",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rcoulomb_switch >= ir->rcoulomb);
 +        }
 +    }
 +    else if (ir->coulombtype == eelCUT || EEL_RF(ir->coulombtype))
 +    {
 +        if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            sprintf(err_buf, "With coulombtype = %s, rcoulomb should be >= rlist unless you use a potential modifier",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rlist > ir->rcoulomb);
 +        }
 +    }
 +
 +    if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT ||
 +        ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT)
 +    {
 +        sprintf(warn_buf,
 +                "The switch/shift interaction settings are just for compatibility; you will get better "
 +                "performance from applying potential modifiers to your interactions!\n");
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if (ir->coulombtype == eelPMESWITCH)
 +    {
 +        if (ir->rcoulomb_switch/ir->rcoulomb < 0.9499)
 +        {
 +            sprintf(warn_buf, "The switching range for %s should be 5%% or less, energy conservation will be good anyhow, since ewald_rtol = %g",
 +                    eel_names[ir->coulombtype],
 +                    ir->ewald_rtol);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->coulombtype == eelPMESWITCH || ir->coulombtype == eelPMEUSER ||
 +            ir->coulombtype == eelPMEUSERSWITCH)
 +        {
 +            sprintf(err_buf, "With coulombtype = %s, rcoulomb must be <= rlist",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rcoulomb > ir->rlist);
 +        }
 +        else if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            if (ir->coulombtype == eelPME || ir->coulombtype == eelP3M_AD)
 +            {
 +                sprintf(err_buf,
 +                        "With coulombtype = %s (without modifier), rcoulomb must be equal to rlist,\n"
 +                        "or rlistlong if nstcalclr=1. For optimal energy conservation,consider using\n"
 +                        "a potential modifier.", eel_names[ir->coulombtype]);
 +                if (ir->nstcalclr == 1)
 +                {
 +                    CHECK(ir->rcoulomb != ir->rlist && ir->rcoulomb != ir->rlistlong);
 +                }
 +                else
 +                {
 +                    CHECK(ir->rcoulomb != ir->rlist);
 +                }
 +            }
 +        }
 +    }
 +
 +    if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype))
 +    {
 +        if (ir->pme_order < 3)
 +        {
 +            warning_error(wi, "pme-order can not be smaller than 3");
 +        }
 +    }
 +
 +    if (ir->nwall == 2 && EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->ewald_geometry == eewg3D)
 +        {
 +            sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s",
 +                    epbc_names[ir->ePBC], eewg_names[eewg3DC]);
 +            warning(wi, warn_buf);
 +        }
 +        /* This check avoids extra pbc coding for exclusion corrections */
 +        sprintf(err_buf, "wall-ewald-zfac should be >= 2");
 +        CHECK(ir->wall_ewald_zfac < 2);
 +    }
 +
 +    if (ir_vdw_switched(ir))
 +    {
 +        sprintf(err_buf, "With switched vdw forces or potentials, rvdw-switch must be < rvdw");
 +        CHECK(ir->rvdw_switch >= ir->rvdw);
 +
 +        if (ir->rvdw_switch < 0.5*ir->rvdw)
 +        {
 +            sprintf(warn_buf, "You are applying a switch function to vdw forces or potentials from %g to %g nm, which is more than half the interaction range, whereas switch functions are intended to act only close to the cut-off.",
 +                    ir->rvdw_switch, ir->rvdw);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +    else if (ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME)
 +    {
 +        if (ir->cutoff_scheme == ecutsGROUP && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(err_buf, "With vdwtype = %s, rvdw must be >= rlist unless you use a potential modifier", evdw_names[ir->vdwtype]);
 +            CHECK(ir->rlist > ir->rvdw);
 +        }
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (((ir->coulomb_modifier != eintmodNONE && ir->rcoulomb == ir->rlist) ||
 +             (ir->vdw_modifier != eintmodNONE && ir->rvdw == ir->rlist)) &&
 +            ir->nstlist != 1)
 +        {
 +            warning_note(wi, "With exact cut-offs, rlist should be "
 +                         "larger than rcoulomb and rvdw, so that there "
 +                         "is a buffer region for particle motion "
 +                         "between neighborsearch steps");
 +        }
 +
 +        if (ir_coulomb_is_zero_at_cutoff(ir) && ir->rlistlong <= ir->rcoulomb)
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rcoulomb.",
 +                    IR_TWINRANGE(*ir) ? "rlistlong" : "rlist");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir_vdw_switched(ir) && (ir->rlistlong <= ir->rvdw))
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rvdw.",
 +                    IR_TWINRANGE(*ir) ? "rlistlong" : "rlist");
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    if (ir->vdwtype == evdwUSER && ir->eDispCorr != edispcNO)
 +    {
 +        warning_note(wi, "You have selected user tables with dispersion correction, the dispersion will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction between rvdw_switch and rvdw will not be double counted). Make sure that you really want dispersion correction to -C6/r^6.");
 +    }
 +
 +    if (ir->nstlist == -1)
 +    {
 +        sprintf(err_buf, "With nstlist=-1 rvdw and rcoulomb should be smaller than rlist to account for diffusion and possibly charge-group radii");
 +        CHECK(ir->rvdw >= ir->rlist || ir->rcoulomb >= ir->rlist);
 +    }
 +    sprintf(err_buf, "nstlist can not be smaller than -1");
 +    CHECK(ir->nstlist < -1);
 +
 +    if (ir->eI == eiLBFGS && (ir->coulombtype == eelCUT || ir->vdwtype == evdwCUT)
 +        && ir->rvdw != 0)
 +    {
 +        warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off.");
 +    }
 +
 +    if (ir->eI == eiLBFGS && ir->nbfgscorr <= 0)
 +    {
 +        warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent.");
 +    }
 +
 +    /* ENERGY CONSERVATION */
 +    if (ir_NVE(ir) && ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (!ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > 0 && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for VdW interactions with NVE, for good energy conservation use vdwtype = %s (possibly with DispCorr)",
 +                    evdw_names[evdwSHIFT]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (!ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > 0)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for electrostatics with NVE, for good energy conservation use coulombtype = %s or %s",
 +                    eel_names[eelPMESWITCH], eel_names[eelRF_ZERO]);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    /* IMPLICIT SOLVENT */
 +    if (ir->coulombtype == eelGB_NOTUSED)
 +    {
 +        ir->coulombtype      = eelCUT;
 +        ir->implicit_solvent = eisGBSA;
 +        fprintf(stderr, "Note: Old option for generalized born electrostatics given:\n"
 +                "Changing coulombtype from \"generalized-born\" to \"cut-off\" and instead\n"
 +                "setting implicit-solvent value to \"GBSA\" in input section.\n");
 +    }
 +
 +    if (ir->sa_algorithm == esaSTILL)
 +    {
 +        sprintf(err_buf, "Still SA algorithm not available yet, use %s or %s instead\n", esa_names[esaAPPROX], esa_names[esaNO]);
 +        CHECK(ir->sa_algorithm == esaSTILL);
 +    }
 +
 +    if (ir->implicit_solvent == eisGBSA)
 +    {
 +        sprintf(err_buf, "With GBSA implicit solvent, rgbradii must be equal to rlist.");
 +        CHECK(ir->rgbradii != ir->rlist);
 +
 +        if (ir->coulombtype != eelCUT)
 +        {
 +            sprintf(err_buf, "With GBSA, coulombtype must be equal to %s\n", eel_names[eelCUT]);
 +            CHECK(ir->coulombtype != eelCUT);
 +        }
 +        if (ir->vdwtype != evdwCUT)
 +        {
 +            sprintf(err_buf, "With GBSA, vdw-type must be equal to %s\n", evdw_names[evdwCUT]);
 +            CHECK(ir->vdwtype != evdwCUT);
 +        }
 +        if (ir->nstgbradii < 1)
 +        {
 +            sprintf(warn_buf, "Using GBSA with nstgbradii<1, setting nstgbradii=1");
 +            warning_note(wi, warn_buf);
 +            ir->nstgbradii = 1;
 +        }
 +        if (ir->sa_algorithm == esaNO)
 +        {
 +            sprintf(warn_buf, "No SA (non-polar) calculation requested together with GB. Are you sure this is what you want?\n");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir->sa_surface_tension < 0 && ir->sa_algorithm != esaNO)
 +        {
 +            sprintf(warn_buf, "Value of sa_surface_tension is < 0. Changing it to 2.05016 or 2.25936 kJ/nm^2/mol for Still and HCT/OBC respectively\n");
 +            warning_note(wi, warn_buf);
 +
 +            if (ir->gb_algorithm == egbSTILL)
 +            {
 +                ir->sa_surface_tension = 0.0049 * CAL2JOULE * 100;
 +            }
 +            else
 +            {
 +                ir->sa_surface_tension = 0.0054 * CAL2JOULE * 100;
 +            }
 +        }
 +        if (ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO)
 +        {
 +            sprintf(err_buf, "Surface tension set to 0 while SA-calculation requested\n");
 +            CHECK(ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO);
 +        }
 +
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        if (ir->cutoff_scheme != ecutsGROUP)
 +        {
 +            warning_error(wi, "AdresS simulation supports only cutoff-scheme=group");
 +        }
 +        if (!EI_SD(ir->eI))
 +        {
 +            warning_error(wi, "AdresS simulation supports only stochastic dynamics");
 +        }
 +        if (ir->epc != epcNO)
 +        {
 +            warning_error(wi, "AdresS simulation does not support pressure coupling");
 +        }
 +        if (EEL_FULL(ir->coulombtype))
 +        {
 +            warning_error(wi, "AdresS simulation does not support long-range electrostatics");
 +        }
 +    }
 +}
 +
 +/* count the number of text elemets separated by whitespace in a string.
 +    str = the input string
 +    maxptr = the maximum number of allowed elements
 +    ptr = the output array of pointers to the first character of each element
 +    returns: the number of elements. */
 +int str_nelem(const char *str, int maxptr, char *ptr[])
 +{
 +    int   np = 0;
 +    char *copy0, *copy;
 +
 +    copy0 = strdup(str);
 +    copy  = copy0;
 +    ltrim(copy);
 +    while (*copy != '\0')
 +    {
 +        if (np >= maxptr)
 +        {
 +            gmx_fatal(FARGS, "Too many groups on line: '%s' (max is %d)",
 +                      str, maxptr);
 +        }
 +        if (ptr)
 +        {
 +            ptr[np] = copy;
 +        }
 +        np++;
 +        while ((*copy != '\0') && !isspace(*copy))
 +        {
 +            copy++;
 +        }
 +        if (*copy != '\0')
 +        {
 +            *copy = '\0';
 +            copy++;
 +        }
 +        ltrim(copy);
 +    }
 +    if (ptr == NULL)
 +    {
 +        sfree(copy0);
 +    }
 +
 +    return np;
 +}
 +
 +/* interpret a number of doubles from a string and put them in an array,
 +   after allocating space for them.
 +   str = the input string
 +   n = the (pre-allocated) number of doubles read
 +   r = the output array of doubles. */
 +static void parse_n_real(char *str, int *n, real **r)
 +{
 +    char *ptr[MAXPTR];
 +    int   i;
 +
 +    *n = str_nelem(str, MAXPTR, ptr);
 +
 +    snew(*r, *n);
 +    for (i = 0; i < *n; i++)
 +    {
 +        (*r)[i] = strtod(ptr[i], NULL);
 +    }
 +}
 +
 +static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN], char weights[STRLEN])
 +{
 +
 +    int         i, j, max_n_lambda, nweights, nfep[efptNR];
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +    real      **count_fep_lambdas;
 +    gmx_bool    bOneLambda = TRUE;
 +
 +    snew(count_fep_lambdas, efptNR);
 +
 +    /* FEP input processing */
 +    /* first, identify the number of lambda values for each type.
 +       All that are nonzero must have the same number */
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        parse_n_real(fep_lambda[i], &(nfep[i]), &(count_fep_lambdas[i]));
 +    }
 +
 +    /* now, determine the number of components.  All must be either zero, or equal. */
 +
 +    max_n_lambda = 0;
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (nfep[i] > max_n_lambda)
 +        {
 +            max_n_lambda = nfep[i];  /* here's a nonzero one.  All of them
 +                                        must have the same number if its not zero.*/
 +            break;
 +        }
 +    }
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (nfep[i] == 0)
 +        {
 +            ir->fepvals->separate_dvdl[i] = FALSE;
 +        }
 +        else if (nfep[i] == max_n_lambda)
 +        {
 +            if (i != efptTEMPERATURE)  /* we treat this differently -- not really a reason to compute the derivative with
 +                                          respect to the temperature currently */
 +            {
 +                ir->fepvals->separate_dvdl[i] = TRUE;
 +            }
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Number of lambdas (%d) for FEP type %s not equal to number of other types (%d)",
 +                      nfep[i], efpt_names[i], max_n_lambda);
 +        }
 +    }
 +    /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */
 +    ir->fepvals->separate_dvdl[efptTEMPERATURE] = FALSE;
 +
 +    /* the number of lambdas is the number we've read in, which is either zero
 +       or the same for all */
 +    fep->n_lambda = max_n_lambda;
 +
 +    /* allocate space for the array of lambda values */
 +    snew(fep->all_lambda, efptNR);
 +    /* if init_lambda is defined, we need to set lambda */
 +    if ((fep->init_lambda > 0) && (fep->n_lambda == 0))
 +    {
 +        ir->fepvals->separate_dvdl[efptFEP] = TRUE;
 +    }
 +    /* otherwise allocate the space for all of the lambdas, and transfer the data */
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        snew(fep->all_lambda[i], fep->n_lambda);
 +        if (nfep[i] > 0)  /* if it's zero, then the count_fep_lambda arrays
 +                             are zero */
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                fep->all_lambda[i][j] = (double)count_fep_lambdas[i][j];
 +            }
 +            sfree(count_fep_lambdas[i]);
 +        }
 +    }
 +    sfree(count_fep_lambdas);
 +
 +    /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for internal
 +       bookkeeping -- for now, init_lambda */
 +
 +    if ((nfep[efptFEP] == 0) && (fep->init_lambda >= 0))
 +    {
 +        for (i = 0; i < fep->n_lambda; i++)
 +        {
 +            fep->all_lambda[efptFEP][i] = fep->init_lambda;
 +        }
 +    }
 +
 +    /* check to see if only a single component lambda is defined, and soft core is defined.
 +       In this case, turn on coulomb soft core */
 +
 +    if (max_n_lambda == 0)
 +    {
 +        bOneLambda = TRUE;
 +    }
 +    else
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            if ((nfep[i] != 0) && (i != efptFEP))
 +            {
 +                bOneLambda = FALSE;
 +            }
 +        }
 +    }
 +    if ((bOneLambda) && (fep->sc_alpha > 0))
 +    {
 +        fep->bScCoul = TRUE;
 +    }
 +
 +    /* Fill in the others with the efptFEP if they are not explicitly
 +       specified (i.e. nfep[i] == 0).  This means if fep is not defined,
 +       they are all zero. */
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if ((nfep[i] == 0) && (i != efptFEP))
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                fep->all_lambda[i][j] = fep->all_lambda[efptFEP][j];
 +            }
 +        }
 +    }
 +
 +
 +    /* make it easier if sc_r_power = 48 by increasing it to the 4th power, to be in the right scale. */
 +    if (fep->sc_r_power == 48)
 +    {
 +        if (fep->sc_alpha > 0.1)
 +        {
 +            gmx_fatal(FARGS, "sc_alpha (%f) for sc_r_power = 48 should usually be between 0.001 and 0.004", fep->sc_alpha);
 +        }
 +    }
 +
 +    expand = ir->expandedvals;
 +    /* now read in the weights */
 +    parse_n_real(weights, &nweights, &(expand->init_lambda_weights));
 +    if (nweights == 0)
 +    {
 +        snew(expand->init_lambda_weights, fep->n_lambda); /* initialize to zero */
 +    }
 +    else if (nweights != fep->n_lambda)
 +    {
 +        gmx_fatal(FARGS, "Number of weights (%d) is not equal to number of lambda values (%d)",
 +                  nweights, fep->n_lambda);
 +    }
 +    if ((expand->nstexpanded < 0) && (ir->efep != efepNO))
 +    {
 +        expand->nstexpanded = fep->nstdhdl;
 +        /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */
 +    }
 +    if ((expand->nstexpanded < 0) && ir->bSimTemp)
 +    {
 +        expand->nstexpanded = 2*(int)(ir->opts.tau_t[0]/ir->delta_t);
 +        /* if you don't specify nstexpanded when doing expanded ensemble simulated tempering, it is set to
 +           2*tau_t just to be careful so it's not to frequent  */
 +    }
 +}
 +
 +
 +static void do_simtemp_params(t_inputrec *ir)
 +{
 +
 +    snew(ir->simtempvals->temperatures, ir->fepvals->n_lambda);
 +    GetSimTemps(ir->fepvals->n_lambda, ir->simtempvals, ir->fepvals->all_lambda[efptTEMPERATURE]);
 +
 +    return;
 +}
 +
 +static void do_wall_params(t_inputrec *ir,
 +                           char *wall_atomtype, char *wall_density,
 +                           t_gromppopts *opts)
 +{
 +    int    nstr, i;
 +    char  *names[MAXPTR];
 +    double dbl;
 +
 +    opts->wall_atomtype[0] = NULL;
 +    opts->wall_atomtype[1] = NULL;
 +
 +    ir->wall_atomtype[0] = -1;
 +    ir->wall_atomtype[1] = -1;
 +    ir->wall_density[0]  = 0;
 +    ir->wall_density[1]  = 0;
 +
 +    if (ir->nwall > 0)
 +    {
 +        nstr = str_nelem(wall_atomtype, MAXPTR, names);
 +        if (nstr != ir->nwall)
 +        {
 +            gmx_fatal(FARGS, "Expected %d elements for wall_atomtype, found %d",
 +                      ir->nwall, nstr);
 +        }
 +        for (i = 0; i < ir->nwall; i++)
 +        {
 +            opts->wall_atomtype[i] = strdup(names[i]);
 +        }
 +
 +        if (ir->wall_type == ewt93 || ir->wall_type == ewt104)
 +        {
 +            nstr = str_nelem(wall_density, MAXPTR, names);
 +            if (nstr != ir->nwall)
 +            {
 +                gmx_fatal(FARGS, "Expected %d elements for wall-density, found %d", ir->nwall, nstr);
 +            }
 +            for (i = 0; i < ir->nwall; i++)
 +            {
 +                sscanf(names[i], "%lf", &dbl);
 +                if (dbl <= 0)
 +                {
 +                    gmx_fatal(FARGS, "wall-density[%d] = %f\n", i, dbl);
 +                }
 +                ir->wall_density[i] = dbl;
 +            }
 +        }
 +    }
 +}
 +
 +static void add_wall_energrps(gmx_groups_t *groups, int nwall, t_symtab *symtab)
 +{
 +    int     i;
 +    t_grps *grps;
 +    char    str[STRLEN];
 +
 +    if (nwall > 0)
 +    {
 +        srenew(groups->grpname, groups->ngrpname+nwall);
 +        grps = &(groups->grps[egcENER]);
 +        srenew(grps->nm_ind, grps->nr+nwall);
 +        for (i = 0; i < nwall; i++)
 +        {
 +            sprintf(str, "wall%d", i);
 +            groups->grpname[groups->ngrpname] = put_symtab(symtab, str);
 +            grps->nm_ind[grps->nr++]          = groups->ngrpname++;
 +        }
 +    }
 +}
 +
 +void read_expandedparams(int *ninp_p, t_inpfile **inp_p,
 +                         t_expanded *expand, warninp_t wi)
 +{
 +    int        ninp, nerror = 0;
 +    t_inpfile *inp;
 +
 +    ninp   = *ninp_p;
 +    inp    = *inp_p;
 +
 +    /* read expanded ensemble parameters */
 +    CCTYPE ("expanded ensemble variables");
 +    ITYPE ("nstexpanded", expand->nstexpanded, -1);
 +    EETYPE("lmc-stats", expand->elamstats, elamstats_names);
 +    EETYPE("lmc-move", expand->elmcmove, elmcmove_names);
 +    EETYPE("lmc-weights-equil", expand->elmceq, elmceq_names);
 +    ITYPE ("weight-equil-number-all-lambda", expand->equil_n_at_lam, -1);
 +    ITYPE ("weight-equil-number-samples", expand->equil_samples, -1);
 +    ITYPE ("weight-equil-number-steps", expand->equil_steps, -1);
 +    RTYPE ("weight-equil-wl-delta", expand->equil_wl_delta, -1);
 +    RTYPE ("weight-equil-count-ratio", expand->equil_ratio, -1);
 +    CCTYPE("Seed for Monte Carlo in lambda space");
 +    ITYPE ("lmc-seed", expand->lmc_seed, -1);
 +    RTYPE ("mc-temperature", expand->mc_temp, -1);
 +    ITYPE ("lmc-repeats", expand->lmc_repeats, 1);
 +    ITYPE ("lmc-gibbsdelta", expand->gibbsdeltalam, -1);
 +    ITYPE ("lmc-forced-nstart", expand->lmc_forced_nstart, 0);
 +    EETYPE("symmetrized-transition-matrix", expand->bSymmetrizedTMatrix, yesno_names);
 +    ITYPE("nst-transition-matrix", expand->nstTij, -1);
 +    ITYPE ("mininum-var-min", expand->minvarmin, 100); /*default is reasonable */
 +    ITYPE ("weight-c-range", expand->c_range, 0);      /* default is just C=0 */
 +    RTYPE ("wl-scale", expand->wl_scale, 0.8);
 +    RTYPE ("wl-ratio", expand->wl_ratio, 0.8);
 +    RTYPE ("init-wl-delta", expand->init_wl_delta, 1.0);
 +    EETYPE("wl-oneovert", expand->bWLoneovert, yesno_names);
 +
 +    *ninp_p   = ninp;
 +    *inp_p    = inp;
 +
 +    return;
 +}
 +
 +void get_ir(const char *mdparin, const char *mdparout,
 +            t_inputrec *ir, t_gromppopts *opts,
 +            warninp_t wi)
 +{
 +    char       *dumstr[2];
 +    double      dumdub[2][6];
 +    t_inpfile  *inp;
 +    const char *tmp;
 +    int         i, j, m, ninp;
 +    char        warn_buf[STRLEN];
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    init_inputrec_strings();
 +    inp = read_inpfile(mdparin, &ninp, wi);
 +
 +    snew(dumstr[0], STRLEN);
 +    snew(dumstr[1], STRLEN);
 +
 +    if (-1 == search_einp(ninp, inp, "cutoff-scheme"))
 +    {
 +        sprintf(warn_buf,
 +                "%s did not specify a value for the .mdp option "
 +                "\"cutoff-scheme\". Probably it was first intended for use "
 +                "with GROMACS before 4.6. In 4.6, the Verlet scheme was "
 +                "introduced, but the group scheme was still the default. "
 +                "The default is now the Verlet scheme, so you will observe "
 +                "different behaviour.", mdparin);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    /* remove the following deprecated commands */
 +    REM_TYPE("title");
 +    REM_TYPE("cpp");
 +    REM_TYPE("domain-decomposition");
 +    REM_TYPE("andersen-seed");
 +    REM_TYPE("dihre");
 +    REM_TYPE("dihre-fc");
 +    REM_TYPE("dihre-tau");
 +    REM_TYPE("nstdihreout");
 +    REM_TYPE("nstcheckpoint");
 +
 +    /* replace the following commands with the clearer new versions*/
 +    REPL_TYPE("unconstrained-start", "continuation");
 +    REPL_TYPE("foreign-lambda", "fep-lambdas");
 +    REPL_TYPE("verlet-buffer-drift", "verlet-buffer-tolerance");
 +    REPL_TYPE("nstxtcout", "nstxout-compressed");
 +    REPL_TYPE("xtc-grps", "compressed-x-grps");
 +    REPL_TYPE("xtc-precision", "compressed-x-precision");
 +
 +    CCTYPE ("VARIOUS PREPROCESSING OPTIONS");
 +    CTYPE ("Preprocessor information: use cpp syntax.");
 +    CTYPE ("e.g.: -I/home/joe/doe -I/home/mary/roe");
 +    STYPE ("include", opts->include,  NULL);
 +    CTYPE ("e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)");
 +    STYPE ("define",  opts->define,   NULL);
 +
 +    CCTYPE ("RUN CONTROL PARAMETERS");
 +    EETYPE("integrator",  ir->eI,         ei_names);
 +    CTYPE ("Start time and timestep in ps");
 +    RTYPE ("tinit",   ir->init_t, 0.0);
 +    RTYPE ("dt",      ir->delta_t,    0.001);
 +    STEPTYPE ("nsteps",   ir->nsteps,     0);
 +    CTYPE ("For exact run continuation or redoing part of a run");
 +    STEPTYPE ("init-step", ir->init_step,  0);
 +    CTYPE ("Part index is updated automatically on checkpointing (keeps files separate)");
 +    ITYPE ("simulation-part", ir->simulation_part, 1);
 +    CTYPE ("mode for center of mass motion removal");
 +    EETYPE("comm-mode",   ir->comm_mode,  ecm_names);
 +    CTYPE ("number of steps for center of mass motion removal");
 +    ITYPE ("nstcomm", ir->nstcomm,    100);
 +    CTYPE ("group(s) for center of mass motion removal");
 +    STYPE ("comm-grps",   is->vcm,            NULL);
 +
 +    CCTYPE ("LANGEVIN DYNAMICS OPTIONS");
 +    CTYPE ("Friction coefficient (amu/ps) and random seed");
 +    RTYPE ("bd-fric",     ir->bd_fric,    0.0);
 +    STEPTYPE ("ld-seed",  ir->ld_seed,    -1);
 +
 +    /* Em stuff */
 +    CCTYPE ("ENERGY MINIMIZATION OPTIONS");
 +    CTYPE ("Force tolerance and initial step-size");
 +    RTYPE ("emtol",       ir->em_tol,     10.0);
 +    RTYPE ("emstep",      ir->em_stepsize, 0.01);
 +    CTYPE ("Max number of iterations in relax-shells");
 +    ITYPE ("niter",       ir->niter,      20);
 +    CTYPE ("Step size (ps^2) for minimization of flexible constraints");
 +    RTYPE ("fcstep",      ir->fc_stepsize, 0);
 +    CTYPE ("Frequency of steepest descents steps when doing CG");
 +    ITYPE ("nstcgsteep",  ir->nstcgsteep, 1000);
 +    ITYPE ("nbfgscorr",   ir->nbfgscorr,  10);
 +
 +    CCTYPE ("TEST PARTICLE INSERTION OPTIONS");
 +    RTYPE ("rtpi",    ir->rtpi,   0.05);
 +
 +    /* Output options */
 +    CCTYPE ("OUTPUT CONTROL OPTIONS");
 +    CTYPE ("Output frequency for coords (x), velocities (v) and forces (f)");
 +    ITYPE ("nstxout", ir->nstxout,    0);
 +    ITYPE ("nstvout", ir->nstvout,    0);
 +    ITYPE ("nstfout", ir->nstfout,    0);
 +    ir->nstcheckpoint = 1000;
 +    CTYPE ("Output frequency for energies to log file and energy file");
 +    ITYPE ("nstlog",  ir->nstlog, 1000);
 +    ITYPE ("nstcalcenergy", ir->nstcalcenergy, 100);
 +    ITYPE ("nstenergy",   ir->nstenergy,  1000);
 +    CTYPE ("Output frequency and precision for .xtc file");
 +    ITYPE ("nstxout-compressed", ir->nstxout_compressed,  0);
 +    RTYPE ("compressed-x-precision", ir->x_compression_precision, 1000.0);
 +    CTYPE ("This selects the subset of atoms for the compressed");
 +    CTYPE ("trajectory file. You can select multiple groups. By");
 +    CTYPE ("default, all atoms will be written.");
 +    STYPE ("compressed-x-grps", is->x_compressed_groups, NULL);
 +    CTYPE ("Selection of energy groups");
 +    STYPE ("energygrps",  is->energy,         NULL);
 +
 +    /* Neighbor searching */
 +    CCTYPE ("NEIGHBORSEARCHING PARAMETERS");
 +    CTYPE ("cut-off scheme (Verlet: particle based cut-offs, group: using charge groups)");
 +    EETYPE("cutoff-scheme",     ir->cutoff_scheme,    ecutscheme_names);
 +    CTYPE ("nblist update frequency");
 +    ITYPE ("nstlist", ir->nstlist,    10);
 +    CTYPE ("ns algorithm (simple or grid)");
 +    EETYPE("ns-type",     ir->ns_type,    ens_names);
 +    /* set ndelta to the optimal value of 2 */
 +    ir->ndelta = 2;
 +    CTYPE ("Periodic boundary conditions: xyz, no, xy");
 +    EETYPE("pbc",         ir->ePBC,       epbc_names);
 +    EETYPE("periodic-molecules", ir->bPeriodicMols, yesno_names);
 +    CTYPE ("Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,");
 +    CTYPE ("a value of -1 means: use rlist");
 +    RTYPE("verlet-buffer-tolerance", ir->verletbuf_tol,    0.005);
 +    CTYPE ("nblist cut-off");
 +    RTYPE ("rlist",   ir->rlist,  1.0);
 +    CTYPE ("long-range cut-off for switched potentials");
 +    RTYPE ("rlistlong",   ir->rlistlong,  -1);
 +    ITYPE ("nstcalclr",   ir->nstcalclr,  -1);
 +
 +    /* Electrostatics */
 +    CCTYPE ("OPTIONS FOR ELECTROSTATICS AND VDW");
 +    CTYPE ("Method for doing electrostatics");
 +    EETYPE("coulombtype", ir->coulombtype,    eel_names);
 +    EETYPE("coulomb-modifier",    ir->coulomb_modifier,    eintmod_names);
 +    CTYPE ("cut-off lengths");
 +    RTYPE ("rcoulomb-switch", ir->rcoulomb_switch,    0.0);
 +    RTYPE ("rcoulomb",    ir->rcoulomb,   1.0);
 +    CTYPE ("Relative dielectric constant for the medium and the reaction field");
 +    RTYPE ("epsilon-r",   ir->epsilon_r,  1.0);
 +    RTYPE ("epsilon-rf",  ir->epsilon_rf, 0.0);
 +    CTYPE ("Method for doing Van der Waals");
 +    EETYPE("vdw-type",    ir->vdwtype,    evdw_names);
 +    EETYPE("vdw-modifier",    ir->vdw_modifier,    eintmod_names);
 +    CTYPE ("cut-off lengths");
 +    RTYPE ("rvdw-switch", ir->rvdw_switch,    0.0);
 +    RTYPE ("rvdw",    ir->rvdw,   1.0);
 +    CTYPE ("Apply long range dispersion corrections for Energy and Pressure");
 +    EETYPE("DispCorr",    ir->eDispCorr,  edispc_names);
 +    CTYPE ("Extension of the potential lookup tables beyond the cut-off");
 +    RTYPE ("table-extension", ir->tabext, 1.0);
 +    CTYPE ("Separate tables between energy group pairs");
 +    STYPE ("energygrp-table", is->egptable,   NULL);
 +    CTYPE ("Spacing for the PME/PPPM FFT grid");
 +    RTYPE ("fourierspacing", ir->fourier_spacing, 0.12);
 +    CTYPE ("FFT grid size, when a value is 0 fourierspacing will be used");
 +    ITYPE ("fourier-nx",  ir->nkx,         0);
 +    ITYPE ("fourier-ny",  ir->nky,         0);
 +    ITYPE ("fourier-nz",  ir->nkz,         0);
 +    CTYPE ("EWALD/PME/PPPM parameters");
 +    ITYPE ("pme-order",   ir->pme_order,   4);
 +    RTYPE ("ewald-rtol",  ir->ewald_rtol, 0.00001);
 +    RTYPE ("ewald-rtol-lj", ir->ewald_rtol_lj, 0.001);
 +    EETYPE("lj-pme-comb-rule", ir->ljpme_combination_rule, eljpme_names);
 +    EETYPE("ewald-geometry", ir->ewald_geometry, eewg_names);
 +    RTYPE ("epsilon-surface", ir->epsilon_surface, 0.0);
 +    EETYPE("optimize-fft", ir->bOptFFT,  yesno_names);
 +
 +    CCTYPE("IMPLICIT SOLVENT ALGORITHM");
 +    EETYPE("implicit-solvent", ir->implicit_solvent, eis_names);
 +
 +    CCTYPE ("GENERALIZED BORN ELECTROSTATICS");
 +    CTYPE ("Algorithm for calculating Born radii");
 +    EETYPE("gb-algorithm", ir->gb_algorithm, egb_names);
 +    CTYPE ("Frequency of calculating the Born radii inside rlist");
 +    ITYPE ("nstgbradii", ir->nstgbradii, 1);
 +    CTYPE ("Cutoff for Born radii calculation; the contribution from atoms");
 +    CTYPE ("between rlist and rgbradii is updated every nstlist steps");
 +    RTYPE ("rgbradii",  ir->rgbradii, 1.0);
 +    CTYPE ("Dielectric coefficient of the implicit solvent");
 +    RTYPE ("gb-epsilon-solvent", ir->gb_epsilon_solvent, 80.0);
 +    CTYPE ("Salt concentration in M for Generalized Born models");
 +    RTYPE ("gb-saltconc",  ir->gb_saltconc, 0.0);
 +    CTYPE ("Scaling factors used in the OBC GB model. Default values are OBC(II)");
 +    RTYPE ("gb-obc-alpha", ir->gb_obc_alpha, 1.0);
 +    RTYPE ("gb-obc-beta", ir->gb_obc_beta, 0.8);
 +    RTYPE ("gb-obc-gamma", ir->gb_obc_gamma, 4.85);
 +    RTYPE ("gb-dielectric-offset", ir->gb_dielectric_offset, 0.009);
 +    EETYPE("sa-algorithm", ir->sa_algorithm, esa_names);
 +    CTYPE ("Surface tension (kJ/mol/nm^2) for the SA (nonpolar surface) part of GBSA");
 +    CTYPE ("The value -1 will set default value for Still/HCT/OBC GB-models.");
 +    RTYPE ("sa-surface-tension", ir->sa_surface_tension, -1);
 +
 +    /* Coupling stuff */
 +    CCTYPE ("OPTIONS FOR WEAK COUPLING ALGORITHMS");
 +    CTYPE ("Temperature coupling");
 +    EETYPE("tcoupl",  ir->etc,        etcoupl_names);
 +    ITYPE ("nsttcouple", ir->nsttcouple,  -1);
 +    ITYPE("nh-chain-length",     ir->opts.nhchainlength, 10);
 +    EETYPE("print-nose-hoover-chain-variables", ir->bPrintNHChains, yesno_names);
 +    CTYPE ("Groups to couple separately");
 +    STYPE ("tc-grps",     is->tcgrps,         NULL);
 +    CTYPE ("Time constant (ps) and reference temperature (K)");
 +    STYPE ("tau-t",   is->tau_t,      NULL);
 +    STYPE ("ref-t",   is->ref_t,      NULL);
 +    CTYPE ("pressure coupling");
 +    EETYPE("pcoupl",  ir->epc,        epcoupl_names);
 +    EETYPE("pcoupltype",  ir->epct,       epcoupltype_names);
 +    ITYPE ("nstpcouple", ir->nstpcouple,  -1);
 +    CTYPE ("Time constant (ps), compressibility (1/bar) and reference P (bar)");
 +    RTYPE ("tau-p",   ir->tau_p,  1.0);
 +    STYPE ("compressibility", dumstr[0],  NULL);
 +    STYPE ("ref-p",       dumstr[1],      NULL);
 +    CTYPE ("Scaling of reference coordinates, No, All or COM");
 +    EETYPE ("refcoord-scaling", ir->refcoord_scaling, erefscaling_names);
 +
 +    /* QMMM */
 +    CCTYPE ("OPTIONS FOR QMMM calculations");
 +    EETYPE("QMMM", ir->bQMMM, yesno_names);
 +    CTYPE ("Groups treated Quantum Mechanically");
 +    STYPE ("QMMM-grps",  is->QMMM,          NULL);
 +    CTYPE ("QM method");
 +    STYPE("QMmethod",     is->QMmethod, NULL);
 +    CTYPE ("QMMM scheme");
 +    EETYPE("QMMMscheme",  ir->QMMMscheme,    eQMMMscheme_names);
 +    CTYPE ("QM basisset");
 +    STYPE("QMbasis",      is->QMbasis, NULL);
 +    CTYPE ("QM charge");
 +    STYPE ("QMcharge",    is->QMcharge, NULL);
 +    CTYPE ("QM multiplicity");
 +    STYPE ("QMmult",      is->QMmult, NULL);
 +    CTYPE ("Surface Hopping");
 +    STYPE ("SH",          is->bSH, NULL);
 +    CTYPE ("CAS space options");
 +    STYPE ("CASorbitals",      is->CASorbitals,   NULL);
 +    STYPE ("CASelectrons",     is->CASelectrons,  NULL);
 +    STYPE ("SAon", is->SAon, NULL);
 +    STYPE ("SAoff", is->SAoff, NULL);
 +    STYPE ("SAsteps", is->SAsteps, NULL);
 +    CTYPE ("Scale factor for MM charges");
 +    RTYPE ("MMChargeScaleFactor", ir->scalefactor, 1.0);
 +    CTYPE ("Optimization of QM subsystem");
 +    STYPE ("bOPT",          is->bOPT, NULL);
 +    STYPE ("bTS",          is->bTS, NULL);
 +
 +    /* Simulated annealing */
 +    CCTYPE("SIMULATED ANNEALING");
 +    CTYPE ("Type of annealing for each temperature group (no/single/periodic)");
 +    STYPE ("annealing",   is->anneal,      NULL);
 +    CTYPE ("Number of time points to use for specifying annealing in each group");
 +    STYPE ("annealing-npoints", is->anneal_npoints, NULL);
 +    CTYPE ("List of times at the annealing points for each group");
 +    STYPE ("annealing-time",       is->anneal_time,       NULL);
 +    CTYPE ("Temp. at each annealing point, for each group.");
 +    STYPE ("annealing-temp",  is->anneal_temp,  NULL);
 +
 +    /* Startup run */
 +    CCTYPE ("GENERATE VELOCITIES FOR STARTUP RUN");
 +    EETYPE("gen-vel",     opts->bGenVel,  yesno_names);
 +    RTYPE ("gen-temp",    opts->tempi,    300.0);
 +    ITYPE ("gen-seed",    opts->seed,     -1);
 +
 +    /* Shake stuff */
 +    CCTYPE ("OPTIONS FOR BONDS");
 +    EETYPE("constraints", opts->nshake,   constraints);
 +    CTYPE ("Type of constraint algorithm");
 +    EETYPE("constraint-algorithm",  ir->eConstrAlg, econstr_names);
 +    CTYPE ("Do not constrain the start configuration");
 +    EETYPE("continuation", ir->bContinuation, yesno_names);
 +    CTYPE ("Use successive overrelaxation to reduce the number of shake iterations");
 +    EETYPE("Shake-SOR", ir->bShakeSOR, yesno_names);
 +    CTYPE ("Relative tolerance of shake");
 +    RTYPE ("shake-tol", ir->shake_tol, 0.0001);
 +    CTYPE ("Highest order in the expansion of the constraint coupling matrix");
 +    ITYPE ("lincs-order", ir->nProjOrder, 4);
 +    CTYPE ("Number of iterations in the final step of LINCS. 1 is fine for");
 +    CTYPE ("normal simulations, but use 2 to conserve energy in NVE runs.");
 +    CTYPE ("For energy minimization with constraints it should be 4 to 8.");
 +    ITYPE ("lincs-iter", ir->nLincsIter, 1);
 +    CTYPE ("Lincs will write a warning to the stderr if in one step a bond");
 +    CTYPE ("rotates over more degrees than");
 +    RTYPE ("lincs-warnangle", ir->LincsWarnAngle, 30.0);
 +    CTYPE ("Convert harmonic bonds to morse potentials");
 +    EETYPE("morse",       opts->bMorse, yesno_names);
 +
 +    /* Energy group exclusions */
 +    CCTYPE ("ENERGY GROUP EXCLUSIONS");
 +    CTYPE ("Pairs of energy groups for which all non-bonded interactions are excluded");
 +    STYPE ("energygrp-excl", is->egpexcl,     NULL);
 +
 +    /* Walls */
 +    CCTYPE ("WALLS");
 +    CTYPE ("Number of walls, type, atom types, densities and box-z scale factor for Ewald");
 +    ITYPE ("nwall", ir->nwall, 0);
 +    EETYPE("wall-type",     ir->wall_type,   ewt_names);
 +    RTYPE ("wall-r-linpot", ir->wall_r_linpot, -1);
 +    STYPE ("wall-atomtype", is->wall_atomtype, NULL);
 +    STYPE ("wall-density",  is->wall_density,  NULL);
 +    RTYPE ("wall-ewald-zfac", ir->wall_ewald_zfac, 3);
 +
 +    /* COM pulling */
 +    CCTYPE("COM PULLING");
 +    CTYPE("Pull type: no, umbrella, constraint or constant-force");
 +    EETYPE("pull",          ir->ePull, epull_names);
 +    if (ir->ePull != epullNO)
 +    {
 +        snew(ir->pull, 1);
 +        is->pull_grp = read_pullparams(&ninp, &inp, ir->pull, &opts->pull_start, wi);
 +    }
 +
 +    /* Enforced rotation */
 +    CCTYPE("ENFORCED ROTATION");
 +    CTYPE("Enforced rotation: No or Yes");
 +    EETYPE("rotation",       ir->bRot, yesno_names);
 +    if (ir->bRot)
 +    {
 +        snew(ir->rot, 1);
 +        is->rot_grp = read_rotparams(&ninp, &inp, ir->rot, wi);
 +    }
 +
 +    /* Refinement */
 +    CCTYPE("NMR refinement stuff");
 +    CTYPE ("Distance restraints type: No, Simple or Ensemble");
 +    EETYPE("disre",       ir->eDisre,     edisre_names);
 +    CTYPE ("Force weighting of pairs in one distance restraint: Conservative or Equal");
 +    EETYPE("disre-weighting", ir->eDisreWeighting, edisreweighting_names);
 +    CTYPE ("Use sqrt of the time averaged times the instantaneous violation");
 +    EETYPE("disre-mixed", ir->bDisreMixed, yesno_names);
 +    RTYPE ("disre-fc",    ir->dr_fc,  1000.0);
 +    RTYPE ("disre-tau",   ir->dr_tau, 0.0);
 +    CTYPE ("Output frequency for pair distances to energy file");
 +    ITYPE ("nstdisreout", ir->nstdisreout, 100);
 +    CTYPE ("Orientation restraints: No or Yes");
 +    EETYPE("orire",       opts->bOrire,   yesno_names);
 +    CTYPE ("Orientation restraints force constant and tau for time averaging");
 +    RTYPE ("orire-fc",    ir->orires_fc,  0.0);
 +    RTYPE ("orire-tau",   ir->orires_tau, 0.0);
 +    STYPE ("orire-fitgrp", is->orirefitgrp,    NULL);
 +    CTYPE ("Output frequency for trace(SD) and S to energy file");
 +    ITYPE ("nstorireout", ir->nstorireout, 100);
 +
 +    /* free energy variables */
 +    CCTYPE ("Free energy variables");
 +    EETYPE("free-energy", ir->efep, efep_names);
 +    STYPE ("couple-moltype",  is->couple_moltype,  NULL);
 +    EETYPE("couple-lambda0", opts->couple_lam0, couple_lam);
 +    EETYPE("couple-lambda1", opts->couple_lam1, couple_lam);
 +    EETYPE("couple-intramol", opts->bCoupleIntra, yesno_names);
 +
 +    RTYPE ("init-lambda", fep->init_lambda, -1); /* start with -1 so
 +                                                    we can recognize if
 +                                                    it was not entered */
 +    ITYPE ("init-lambda-state", fep->init_fep_state, -1);
 +    RTYPE ("delta-lambda", fep->delta_lambda, 0.0);
 +    ITYPE ("nstdhdl", fep->nstdhdl, 50);
 +    STYPE ("fep-lambdas", is->fep_lambda[efptFEP], NULL);
 +    STYPE ("mass-lambdas", is->fep_lambda[efptMASS], NULL);
 +    STYPE ("coul-lambdas", is->fep_lambda[efptCOUL], NULL);
 +    STYPE ("vdw-lambdas", is->fep_lambda[efptVDW], NULL);
 +    STYPE ("bonded-lambdas", is->fep_lambda[efptBONDED], NULL);
 +    STYPE ("restraint-lambdas", is->fep_lambda[efptRESTRAINT], NULL);
 +    STYPE ("temperature-lambdas", is->fep_lambda[efptTEMPERATURE], NULL);
 +    ITYPE ("calc-lambda-neighbors", fep->lambda_neighbors, 1);
 +    STYPE ("init-lambda-weights", is->lambda_weights, NULL);
 +    EETYPE("dhdl-print-energy", fep->bPrintEnergy, yesno_names);
 +    RTYPE ("sc-alpha", fep->sc_alpha, 0.0);
 +    ITYPE ("sc-power", fep->sc_power, 1);
 +    RTYPE ("sc-r-power", fep->sc_r_power, 6.0);
 +    RTYPE ("sc-sigma", fep->sc_sigma, 0.3);
 +    EETYPE("sc-coul", fep->bScCoul, yesno_names);
 +    ITYPE ("dh_hist_size", fep->dh_hist_size, 0);
 +    RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1);
 +    EETYPE("separate-dhdl-file", fep->separate_dhdl_file,
 +           separate_dhdl_file_names);
 +    EETYPE("dhdl-derivatives", fep->dhdl_derivatives, dhdl_derivatives_names);
 +    ITYPE ("dh_hist_size", fep->dh_hist_size, 0);
 +    RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1);
 +
 +    /* Non-equilibrium MD stuff */
 +    CCTYPE("Non-equilibrium MD stuff");
 +    STYPE ("acc-grps",    is->accgrps,        NULL);
 +    STYPE ("accelerate",  is->acc,            NULL);
 +    STYPE ("freezegrps",  is->freeze,         NULL);
 +    STYPE ("freezedim",   is->frdim,          NULL);
 +    RTYPE ("cos-acceleration", ir->cos_accel, 0);
 +    STYPE ("deform",      is->deform,         NULL);
 +
 +    /* simulated tempering variables */
 +    CCTYPE("simulated tempering variables");
 +    EETYPE("simulated-tempering", ir->bSimTemp, yesno_names);
 +    EETYPE("simulated-tempering-scaling", ir->simtempvals->eSimTempScale, esimtemp_names);
 +    RTYPE("sim-temp-low", ir->simtempvals->simtemp_low, 300.0);
 +    RTYPE("sim-temp-high", ir->simtempvals->simtemp_high, 300.0);
 +
 +    /* expanded ensemble variables */
 +    if (ir->efep == efepEXPANDED || ir->bSimTemp)
 +    {
 +        read_expandedparams(&ninp, &inp, expand, wi);
 +    }
 +
 +    /* Electric fields */
 +    CCTYPE("Electric fields");
 +    CTYPE ("Format is number of terms (int) and for all terms an amplitude (real)");
 +    CTYPE ("and a phase angle (real)");
 +    STYPE ("E-x",     is->efield_x,   NULL);
 +    STYPE ("E-xt",    is->efield_xt,  NULL);
 +    STYPE ("E-y",     is->efield_y,   NULL);
 +    STYPE ("E-yt",    is->efield_yt,  NULL);
 +    STYPE ("E-z",     is->efield_z,   NULL);
 +    STYPE ("E-zt",    is->efield_zt,  NULL);
 +
 +    CCTYPE("Ion/water position swapping for computational electrophysiology setups");
 +    CTYPE("Swap positions along direction: no, X, Y, Z");
 +    EETYPE("swapcoords", ir->eSwapCoords, eSwapTypes_names);
 +    if (ir->eSwapCoords != eswapNO)
 +    {
 +        snew(ir->swap, 1);
 +        CTYPE("Swap attempt frequency");
 +        ITYPE("swap-frequency", ir->swap->nstswap, 1);
 +        CTYPE("Two index groups that contain the compartment-partitioning atoms");
 +        STYPE("split-group0", splitgrp0, NULL);
 +        STYPE("split-group1", splitgrp1, NULL);
 +        CTYPE("Use center of mass of split groups (yes/no), otherwise center of geometry is used");
 +        EETYPE("massw-split0", ir->swap->massw_split[0], yesno_names);
 +        EETYPE("massw-split1", ir->swap->massw_split[1], yesno_names);
 +
 +        CTYPE("Group name of ions that can be exchanged with solvent molecules");
 +        STYPE("swap-group", swapgrp, NULL);
 +        CTYPE("Group name of solvent molecules");
 +        STYPE("solvent-group", solgrp, NULL);
 +
 +        CTYPE("Split cylinder: radius, upper and lower extension (nm) (this will define the channels)");
 +        CTYPE("Note that the split cylinder settings do not have an influence on the swapping protocol,");
 +        CTYPE("however, if correctly defined, the ion permeation events are counted per channel");
 +        RTYPE("cyl0-r", ir->swap->cyl0r, 2.0);
 +        RTYPE("cyl0-up", ir->swap->cyl0u, 1.0);
 +        RTYPE("cyl0-down", ir->swap->cyl0l, 1.0);
 +        RTYPE("cyl1-r", ir->swap->cyl1r, 2.0);
 +        RTYPE("cyl1-up", ir->swap->cyl1u, 1.0);
 +        RTYPE("cyl1-down", ir->swap->cyl1l, 1.0);
 +
 +        CTYPE("Average the number of ions per compartment over these many swap attempt steps");
 +        ITYPE("coupl-steps", ir->swap->nAverage, 10);
 +        CTYPE("Requested number of anions and cations for each of the two compartments");
 +        CTYPE("-1 means fix the numbers as found in time step 0");
 +        ITYPE("anionsA", ir->swap->nanions[0], -1);
 +        ITYPE("cationsA", ir->swap->ncations[0], -1);
 +        ITYPE("anionsB", ir->swap->nanions[1], -1);
 +        ITYPE("cationsB", ir->swap->ncations[1], -1);
 +        CTYPE("Start to swap ions if threshold difference to requested count is reached");
 +        RTYPE("threshold", ir->swap->threshold, 1.0);
 +    }
 +
 +    /* AdResS defined thingies */
 +    CCTYPE ("AdResS parameters");
 +    EETYPE("adress",       ir->bAdress, yesno_names);
 +    if (ir->bAdress)
 +    {
 +        snew(ir->adress, 1);
 +        read_adressparams(&ninp, &inp, ir->adress, wi);
 +    }
 +
 +    /* User defined thingies */
 +    CCTYPE ("User defined thingies");
 +    STYPE ("user1-grps",  is->user1,          NULL);
 +    STYPE ("user2-grps",  is->user2,          NULL);
 +    ITYPE ("userint1",    ir->userint1,   0);
 +    ITYPE ("userint2",    ir->userint2,   0);
 +    ITYPE ("userint3",    ir->userint3,   0);
 +    ITYPE ("userint4",    ir->userint4,   0);
 +    RTYPE ("userreal1",   ir->userreal1,  0);
 +    RTYPE ("userreal2",   ir->userreal2,  0);
 +    RTYPE ("userreal3",   ir->userreal3,  0);
 +    RTYPE ("userreal4",   ir->userreal4,  0);
 +#undef CTYPE
 +
 +    write_inpfile(mdparout, ninp, inp, FALSE, wi);
 +    for (i = 0; (i < ninp); i++)
 +    {
 +        sfree(inp[i].name);
 +        sfree(inp[i].value);
 +    }
 +    sfree(inp);
 +
 +    /* Process options if necessary */
 +    for (m = 0; m < 2; m++)
 +    {
 +        for (i = 0; i < 2*DIM; i++)
 +        {
 +            dumdub[m][i] = 0.0;
 +        }
 +        if (ir->epc)
 +        {
 +            switch (ir->epct)
 +            {
 +                case epctISOTROPIC:
 +                    if (sscanf(dumstr[m], "%lf", &(dumdub[m][XX])) != 1)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 1)");
 +                    }
 +                    dumdub[m][YY] = dumdub[m][ZZ] = dumdub[m][XX];
 +                    break;
 +                case epctSEMIISOTROPIC:
 +                case epctSURFACETENSION:
 +                    if (sscanf(dumstr[m], "%lf%lf",
 +                               &(dumdub[m][XX]), &(dumdub[m][ZZ])) != 2)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 2)");
 +                    }
 +                    dumdub[m][YY] = dumdub[m][XX];
 +                    break;
 +                case epctANISOTROPIC:
 +                    if (sscanf(dumstr[m], "%lf%lf%lf%lf%lf%lf",
 +                               &(dumdub[m][XX]), &(dumdub[m][YY]), &(dumdub[m][ZZ]),
 +                               &(dumdub[m][3]), &(dumdub[m][4]), &(dumdub[m][5])) != 6)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 6)");
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Pressure coupling type %s not implemented yet",
 +                              epcoupltype_names[ir->epct]);
 +            }
 +        }
 +    }
 +    clear_mat(ir->ref_p);
 +    clear_mat(ir->compress);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        ir->ref_p[i][i]    = dumdub[1][i];
 +        ir->compress[i][i] = dumdub[0][i];
 +    }
 +    if (ir->epct == epctANISOTROPIC)
 +    {
 +        ir->ref_p[XX][YY] = dumdub[1][3];
 +        ir->ref_p[XX][ZZ] = dumdub[1][4];
 +        ir->ref_p[YY][ZZ] = dumdub[1][5];
 +        if (ir->ref_p[XX][YY] != 0 && ir->ref_p[XX][ZZ] != 0 && ir->ref_p[YY][ZZ] != 0)
 +        {
 +            warning(wi, "All off-diagonal reference pressures are non-zero. Are you sure you want to apply a threefold shear stress?\n");
 +        }
 +        ir->compress[XX][YY] = dumdub[0][3];
 +        ir->compress[XX][ZZ] = dumdub[0][4];
 +        ir->compress[YY][ZZ] = dumdub[0][5];
 +        for (i = 0; i < DIM; i++)
 +        {
 +            for (m = 0; m < i; m++)
 +            {
 +                ir->ref_p[i][m]    = ir->ref_p[m][i];
 +                ir->compress[i][m] = ir->compress[m][i];
 +            }
 +        }
 +    }
 +
 +    if (ir->comm_mode == ecmNO)
 +    {
 +        ir->nstcomm = 0;
 +    }
 +
 +    opts->couple_moltype = NULL;
 +    if (strlen(is->couple_moltype) > 0)
 +    {
 +        if (ir->efep != efepNO)
 +        {
 +            opts->couple_moltype = strdup(is->couple_moltype);
 +            if (opts->couple_lam0 == opts->couple_lam1)
 +            {
 +                warning(wi, "The lambda=0 and lambda=1 states for coupling are identical");
 +            }
 +            if (ir->eI == eiMD && (opts->couple_lam0 == ecouplamNONE ||
 +                                   opts->couple_lam1 == ecouplamNONE))
 +            {
 +                warning(wi, "For proper sampling of the (nearly) decoupled state, stochastic dynamics should be used");
 +            }
 +        }
 +        else
 +        {
 +            warning(wi, "Can not couple a molecule with free_energy = no");
 +        }
 +    }
 +    /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
 +    if (ir->efep != efepNO)
 +    {
 +        if (fep->delta_lambda > 0)
 +        {
 +            ir->efep = efepSLOWGROWTH;
 +        }
 +    }
 +
 +    if (ir->bSimTemp)
 +    {
 +        fep->bPrintEnergy = TRUE;
 +        /* always print out the energy to dhdl if we are doing expanded ensemble, since we need the total energy
 +           if the temperature is changing. */
 +    }
 +
 +    if ((ir->efep != efepNO) || ir->bSimTemp)
 +    {
 +        ir->bExpanded = FALSE;
 +        if ((ir->efep == efepEXPANDED) || ir->bSimTemp)
 +        {
 +            ir->bExpanded = TRUE;
 +        }
 +        do_fep_params(ir, is->fep_lambda, is->lambda_weights);
 +        if (ir->bSimTemp) /* done after fep params */
 +        {
 +            do_simtemp_params(ir);
 +        }
 +    }
 +    else
 +    {
 +        ir->fepvals->n_lambda = 0;
 +    }
 +
 +    /* WALL PARAMETERS */
 +
 +    do_wall_params(ir, is->wall_atomtype, is->wall_density, opts);
 +
 +    /* ORIENTATION RESTRAINT PARAMETERS */
 +
 +    if (opts->bOrire && str_nelem(is->orirefitgrp, MAXPTR, NULL) != 1)
 +    {
 +        warning_error(wi, "ERROR: Need one orientation restraint fit group\n");
 +    }
 +
 +    /* DEFORMATION PARAMETERS */
 +
 +    clear_mat(ir->deform);
 +    for (i = 0; i < 6; i++)
 +    {
 +        dumdub[0][i] = 0;
 +    }
 +    m = sscanf(is->deform, "%lf %lf %lf %lf %lf %lf",
 +               &(dumdub[0][0]), &(dumdub[0][1]), &(dumdub[0][2]),
 +               &(dumdub[0][3]), &(dumdub[0][4]), &(dumdub[0][5]));
 +    for (i = 0; i < 3; i++)
 +    {
 +        ir->deform[i][i] = dumdub[0][i];
 +    }
 +    ir->deform[YY][XX] = dumdub[0][3];
 +    ir->deform[ZZ][XX] = dumdub[0][4];
 +    ir->deform[ZZ][YY] = dumdub[0][5];
 +    if (ir->epc != epcNO)
 +    {
 +        for (i = 0; i < 3; i++)
 +        {
 +            for (j = 0; j <= i; j++)
 +            {
 +                if (ir->deform[i][j] != 0 && ir->compress[i][j] != 0)
 +                {
 +                    warning_error(wi, "A box element has deform set and compressibility > 0");
 +                }
 +            }
 +        }
 +        for (i = 0; i < 3; i++)
 +        {
 +            for (j = 0; j < i; j++)
 +            {
 +                if (ir->deform[i][j] != 0)
 +                {
 +                    for (m = j; m < DIM; m++)
 +                    {
 +                        if (ir->compress[m][j] != 0)
 +                        {
 +                            sprintf(warn_buf, "An off-diagonal box element has deform set while compressibility > 0 for the same component of another box vector, this might lead to spurious periodicity effects.");
 +                            warning(wi, warn_buf);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Ion/water position swapping checks */
 +    if (ir->eSwapCoords != eswapNO)
 +    {
 +        if (ir->swap->nstswap < 1)
 +        {
 +            warning_error(wi, "swap_frequency must be 1 or larger when ion swapping is requested");
 +        }
 +        if (ir->swap->nAverage < 1)
 +        {
 +            warning_error(wi, "coupl_steps must be 1 or larger.\n");
 +        }
 +        if (ir->swap->threshold < 1.0)
 +        {
 +            warning_error(wi, "Ion count threshold must be at least 1.\n");
 +        }
 +    }
 +
 +    sfree(dumstr[0]);
 +    sfree(dumstr[1]);
 +}
 +
 +static int search_QMstring(const char *s, int ng, const char *gn[])
 +{
 +    /* same as normal search_string, but this one searches QM strings */
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS, "this QM method or basisset (%s) is not implemented\n!", s);
 +
 +    return -1;
 +
 +} /* search_QMstring */
 +
 +/* We would like gn to be const as well, but C doesn't allow this */
 +int search_string(const char *s, int ng, char *gn[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS,
 +              "Group %s referenced in the .mdp file was not found in the index file.\n"
 +              "Group names must match either [moleculetype] names or custom index group\n"
 +              "names, in which case you must supply an index file to the '-n' option\n"
 +              "of grompp.",
 +              s);
 +
 +    return -1;
 +}
 +
 +static gmx_bool do_numbering(int natoms, gmx_groups_t *groups, int ng, char *ptrs[],
 +                             t_blocka *block, char *gnames[],
 +                             int gtype, int restnm,
 +                             int grptp, gmx_bool bVerbose,
 +                             warninp_t wi)
 +{
 +    unsigned short *cbuf;
 +    t_grps         *grps = &(groups->grps[gtype]);
 +    int             i, j, gid, aj, ognr, ntot = 0;
 +    const char     *title;
 +    gmx_bool        bRest;
 +    char            warn_buf[STRLEN];
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Starting numbering %d groups of type %d\n", ng, gtype);
 +    }
 +
 +    title = gtypes[gtype];
 +
 +    snew(cbuf, natoms);
 +    /* Mark all id's as not set */
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        cbuf[i] = NOGID;
 +    }
 +
 +    snew(grps->nm_ind, ng+1); /* +1 for possible rest group */
 +    for (i = 0; (i < ng); i++)
 +    {
 +        /* Lookup the group name in the block structure */
 +        gid = search_string(ptrs[i], block->nr, gnames);
 +        if ((grptp != egrptpONE) || (i == 0))
 +        {
 +            grps->nm_ind[grps->nr++] = gid;
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Found gid %d for group %s\n", gid, ptrs[i]);
 +        }
 +
 +        /* Now go over the atoms in the group */
 +        for (j = block->index[gid]; (j < block->index[gid+1]); j++)
 +        {
 +
 +            aj = block->a[j];
 +
 +            /* Range checking */
 +            if ((aj < 0) || (aj >= natoms))
 +            {
 +                gmx_fatal(FARGS, "Invalid atom number %d in indexfile", aj);
 +            }
 +            /* Lookup up the old group number */
 +            ognr = cbuf[aj];
 +            if (ognr != NOGID)
 +            {
 +                gmx_fatal(FARGS, "Atom %d in multiple %s groups (%d and %d)",
 +                          aj+1, title, ognr+1, i+1);
 +            }
 +            else
 +            {
 +                /* Store the group number in buffer */
 +                if (grptp == egrptpONE)
 +                {
 +                    cbuf[aj] = 0;
 +                }
 +                else
 +                {
 +                    cbuf[aj] = i;
 +                }
 +                ntot++;
 +            }
 +        }
 +    }
 +
 +    /* Now check whether we have done all atoms */
 +    bRest = FALSE;
 +    if (ntot != natoms)
 +    {
 +        if (grptp == egrptpALL)
 +        {
 +            gmx_fatal(FARGS, "%d atoms are not part of any of the %s groups",
 +                      natoms-ntot, title);
 +        }
 +        else if (grptp == egrptpPART)
 +        {
 +            sprintf(warn_buf, "%d atoms are not part of any of the %s groups",
 +                    natoms-ntot, title);
 +            warning_note(wi, warn_buf);
 +        }
 +        /* Assign all atoms currently unassigned to a rest group */
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            if (cbuf[j] == NOGID)
 +            {
 +                cbuf[j] = grps->nr;
 +                bRest   = TRUE;
 +            }
 +        }
 +        if (grptp != egrptpPART)
 +        {
 +            if (bVerbose)
 +            {
 +                fprintf(stderr,
 +                        "Making dummy/rest group for %s containing %d elements\n",
 +                        title, natoms-ntot);
 +            }
 +            /* Add group name "rest" */
 +            grps->nm_ind[grps->nr] = restnm;
 +
 +            /* Assign the rest name to all atoms not currently assigned to a group */
 +            for (j = 0; (j < natoms); j++)
 +            {
 +                if (cbuf[j] == NOGID)
 +                {
 +                    cbuf[j] = grps->nr;
 +                }
 +            }
 +            grps->nr++;
 +        }
 +    }
 +
 +    if (grps->nr == 1 && (ntot == 0 || ntot == natoms))
 +    {
 +        /* All atoms are part of one (or no) group, no index required */
 +        groups->ngrpnr[gtype] = 0;
 +        groups->grpnr[gtype]  = NULL;
 +    }
 +    else
 +    {
 +        groups->ngrpnr[gtype] = natoms;
 +        snew(groups->grpnr[gtype], natoms);
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            groups->grpnr[gtype][j] = cbuf[j];
 +        }
 +    }
 +
 +    sfree(cbuf);
 +
 +    return (bRest && grptp == egrptpPART);
 +}
 +
 +static void calc_nrdf(gmx_mtop_t *mtop, t_inputrec *ir, char **gnames)
 +{
 +    t_grpopts              *opts;
 +    gmx_groups_t           *groups;
 +    t_pull                 *pull;
 +    int                     natoms, ai, aj, i, j, d, g, imin, jmin;
 +    t_iatom                *ia;
 +    int                    *nrdf2, *na_vcm, na_tot;
 +    double                 *nrdf_tc, *nrdf_vcm, nrdf_uc, n_sub = 0;
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     mb, mol, ftype, as;
 +    gmx_molblock_t         *molb;
 +    gmx_moltype_t          *molt;
 +
 +    /* Calculate nrdf.
 +     * First calc 3xnr-atoms for each group
 +     * then subtract half a degree of freedom for each constraint
 +     *
 +     * Only atoms and nuclei contribute to the degrees of freedom...
 +     */
 +
 +    opts = &ir->opts;
 +
 +    groups = &mtop->groups;
 +    natoms = mtop->natoms;
 +
 +    /* Allocate one more for a possible rest group */
 +    /* We need to sum degrees of freedom into doubles,
 +     * since floats give too low nrdf's above 3 million atoms.
 +     */
 +    snew(nrdf_tc, groups->grps[egcTC].nr+1);
 +    snew(nrdf_vcm, groups->grps[egcVCM].nr+1);
 +    snew(na_vcm, groups->grps[egcVCM].nr+1);
 +
 +    for (i = 0; i < groups->grps[egcTC].nr; i++)
 +    {
 +        nrdf_tc[i] = 0;
 +    }
 +    for (i = 0; i < groups->grps[egcVCM].nr+1; i++)
 +    {
 +        nrdf_vcm[i] = 0;
 +    }
 +
 +    snew(nrdf2, natoms);
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +    {
 +        nrdf2[i] = 0;
 +        if (atom->ptype == eptAtom || atom->ptype == eptNucleus)
 +        {
 +            g = ggrpnr(groups, egcFREEZE, i);
 +            /* Double count nrdf for particle i */
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (opts->nFreeze[g][d] == 0)
 +                {
 +                    nrdf2[i] += 2;
 +                }
 +            }
 +            nrdf_tc [ggrpnr(groups, egcTC, i)]  += 0.5*nrdf2[i];
 +            nrdf_vcm[ggrpnr(groups, egcVCM, i)] += 0.5*nrdf2[i];
 +        }
 +    }
 +
 +    as = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        molt = &mtop->moltype[molb->type];
 +        atom = molt->atoms.atom;
 +        for (mol = 0; mol < molb->nmol; mol++)
 +        {
 +            for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
 +            {
 +                ia = molt->ilist[ftype].iatoms;
 +                for (i = 0; i < molt->ilist[ftype].nr; )
 +                {
 +                    /* Subtract degrees of freedom for the constraints,
 +                     * if the particles still have degrees of freedom left.
 +                     * If one of the particles is a vsite or a shell, then all
 +                     * constraint motion will go there, but since they do not
 +                     * contribute to the constraints the degrees of freedom do not
 +                     * change.
 +                     */
 +                    ai = as + ia[1];
 +                    aj = as + ia[2];
 +                    if (((atom[ia[1]].ptype == eptNucleus) ||
 +                         (atom[ia[1]].ptype == eptAtom)) &&
 +                        ((atom[ia[2]].ptype == eptNucleus) ||
 +                         (atom[ia[2]].ptype == eptAtom)))
 +                    {
 +                        if (nrdf2[ai] > 0)
 +                        {
 +                            jmin = 1;
 +                        }
 +                        else
 +                        {
 +                            jmin = 2;
 +                        }
 +                        if (nrdf2[aj] > 0)
 +                        {
 +                            imin = 1;
 +                        }
 +                        else
 +                        {
 +                            imin = 2;
 +                        }
 +                        imin       = min(imin, nrdf2[ai]);
 +                        jmin       = min(jmin, nrdf2[aj]);
 +                        nrdf2[ai] -= imin;
 +                        nrdf2[aj] -= jmin;
 +                        nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                        nrdf_tc [ggrpnr(groups, egcTC, aj)]  -= 0.5*jmin;
 +                        nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                        nrdf_vcm[ggrpnr(groups, egcVCM, aj)] -= 0.5*jmin;
 +                    }
 +                    ia += interaction_function[ftype].nratoms+1;
 +                    i  += interaction_function[ftype].nratoms+1;
 +                }
 +            }
 +            ia = molt->ilist[F_SETTLE].iatoms;
 +            for (i = 0; i < molt->ilist[F_SETTLE].nr; )
 +            {
 +                /* Subtract 1 dof from every atom in the SETTLE */
 +                for (j = 0; j < 3; j++)
 +                {
 +                    ai         = as + ia[1+j];
 +                    imin       = min(2, nrdf2[ai]);
 +                    nrdf2[ai] -= imin;
 +                    nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                    nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                }
 +                ia += 4;
 +                i  += 4;
 +            }
 +            as += molt->atoms.nr;
 +        }
 +    }
 +
 +    if (ir->ePull == epullCONSTRAINT)
 +    {
 +        /* Correct nrdf for the COM constraints.
 +         * We correct using the TC and VCM group of the first atom
 +         * in the reference and pull group. If atoms in one pull group
 +         * belong to different TC or VCM groups it is anyhow difficult
 +         * to determine the optimal nrdf assignment.
 +         */
 +        pull = ir->pull;
 +
 +        for (i = 0; i < pull->ncoord; i++)
 +        {
 +            imin = 1;
 +
 +            for (j = 0; j < 2; j++)
 +            {
 +                const t_pull_group *pgrp;
 +
 +                pgrp = &pull->group[pull->coord[i].group[j]];
 +
 +                if (pgrp->nat > 0)
 +                {
 +                    /* Subtract 1/2 dof from each group */
 +                    ai = pgrp->ind[0];
 +                    nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                    nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                    if (nrdf_tc[ggrpnr(groups, egcTC, ai)] < 0)
 +                    {
 +                        gmx_fatal(FARGS, "Center of mass pulling constraints caused the number of degrees of freedom for temperature coupling group %s to be negative", gnames[groups->grps[egcTC].nm_ind[ggrpnr(groups, egcTC, ai)]]);
 +                    }
 +                }
 +                else
 +                {
 +                    /* We need to subtract the whole DOF from group j=1 */
 +                    imin += 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->nstcomm != 0)
 +    {
 +        /* Subtract 3 from the number of degrees of freedom in each vcm group
 +         * when com translation is removed and 6 when rotation is removed
 +         * as well.
 +         */
 +        switch (ir->comm_mode)
 +        {
 +            case ecmLINEAR:
 +                n_sub = ndof_com(ir);
 +                break;
 +            case ecmANGULAR:
 +                n_sub = 6;
 +                break;
 +            default:
 +                n_sub = 0;
 +                gmx_incons("Checking comm_mode");
 +        }
 +
 +        for (i = 0; i < groups->grps[egcTC].nr; i++)
 +        {
 +            /* Count the number of atoms of TC group i for every VCM group */
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
 +                na_vcm[j] = 0;
 +            }
 +            na_tot = 0;
 +            for (ai = 0; ai < natoms; ai++)
 +            {
 +                if (ggrpnr(groups, egcTC, ai) == i)
 +                {
 +                    na_vcm[ggrpnr(groups, egcVCM, ai)]++;
 +                    na_tot++;
 +                }
 +            }
 +            /* Correct for VCM removal according to the fraction of each VCM
 +             * group present in this TC group.
 +             */
 +            nrdf_uc = nrdf_tc[i];
 +            if (debug)
 +            {
 +                fprintf(debug, "T-group[%d] nrdf_uc = %g, n_sub = %g\n",
 +                        i, nrdf_uc, n_sub);
 +            }
 +            nrdf_tc[i] = 0;
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
 +                if (nrdf_vcm[j] > n_sub)
 +                {
 +                    nrdf_tc[i] += nrdf_uc*((double)na_vcm[j]/(double)na_tot)*
 +                        (nrdf_vcm[j] - n_sub)/nrdf_vcm[j];
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "  nrdf_vcm[%d] = %g, nrdf = %g\n",
 +                            j, nrdf_vcm[j], nrdf_tc[i]);
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; (i < groups->grps[egcTC].nr); i++)
 +    {
 +        opts->nrdf[i] = nrdf_tc[i];
 +        if (opts->nrdf[i] < 0)
 +        {
 +            opts->nrdf[i] = 0;
 +        }
 +        fprintf(stderr,
 +                "Number of degrees of freedom in T-Coupling group %s is %.2f\n",
 +                gnames[groups->grps[egcTC].nm_ind[i]], opts->nrdf[i]);
 +    }
 +
 +    sfree(nrdf2);
 +    sfree(nrdf_tc);
 +    sfree(nrdf_vcm);
 +    sfree(na_vcm);
 +}
 +
 +static void decode_cos(char *s, t_cosines *cosine)
 +{
 +    char   *t;
 +    char    format[STRLEN], f1[STRLEN];
 +    double  a, phi;
 +    int     i;
 +
 +    t = strdup(s);
 +    trim(t);
 +
 +    cosine->n   = 0;
 +    cosine->a   = NULL;
 +    cosine->phi = NULL;
 +    if (strlen(t))
 +    {
 +        sscanf(t, "%d", &(cosine->n));
 +        if (cosine->n <= 0)
 +        {
 +            cosine->n = 0;
 +        }
 +        else
 +        {
 +            snew(cosine->a, cosine->n);
 +            snew(cosine->phi, cosine->n);
 +
 +            sprintf(format, "%%*d");
 +            for (i = 0; (i < cosine->n); i++)
 +            {
 +                strcpy(f1, format);
 +                strcat(f1, "%lf%lf");
 +                if (sscanf(t, f1, &a, &phi) < 2)
 +                {
 +                    gmx_fatal(FARGS, "Invalid input for electric field shift: '%s'", t);
 +                }
 +                cosine->a[i]   = a;
 +                cosine->phi[i] = phi;
 +                strcat(format, "%*lf%*lf");
 +            }
 +        }
 +    }
 +    sfree(t);
 +}
 +
 +static gmx_bool do_egp_flag(t_inputrec *ir, gmx_groups_t *groups,
 +                            const char *option, const char *val, int flag)
 +{
 +    /* The maximum number of energy group pairs would be MAXPTR*(MAXPTR+1)/2.
 +     * But since this is much larger than STRLEN, such a line can not be parsed.
 +     * The real maximum is the number of names that fit in a string: STRLEN/2.
 +     */
 +#define EGP_MAX (STRLEN/2)
 +    int      nelem, i, j, k, nr;
 +    char    *names[EGP_MAX];
 +    char  ***gnames;
 +    gmx_bool bSet;
 +
 +    gnames = groups->grpname;
 +
 +    nelem = str_nelem(val, EGP_MAX, names);
 +    if (nelem % 2 != 0)
 +    {
 +        gmx_fatal(FARGS, "The number of groups for %s is odd", option);
 +    }
 +    nr   = groups->grps[egcENER].nr;
 +    bSet = FALSE;
 +    for (i = 0; i < nelem/2; i++)
 +    {
 +        j = 0;
 +        while ((j < nr) &&
 +               gmx_strcasecmp(names[2*i], *(gnames[groups->grps[egcENER].nm_ind[j]])))
 +        {
 +            j++;
 +        }
 +        if (j == nr)
 +        {
 +            gmx_fatal(FARGS, "%s in %s is not an energy group\n",
 +                      names[2*i], option);
 +        }
 +        k = 0;
 +        while ((k < nr) &&
 +               gmx_strcasecmp(names[2*i+1], *(gnames[groups->grps[egcENER].nm_ind[k]])))
 +        {
 +            k++;
 +        }
 +        if (k == nr)
 +        {
 +            gmx_fatal(FARGS, "%s in %s is not an energy group\n",
 +                      names[2*i+1], option);
 +        }
 +        if ((j < nr) && (k < nr))
 +        {
 +            ir->opts.egp_flags[nr*j+k] |= flag;
 +            ir->opts.egp_flags[nr*k+j] |= flag;
 +            bSet = TRUE;
 +        }
 +    }
 +
 +    return bSet;
 +}
 +
 +
 +static void make_swap_groups(
 +        t_swapcoords *swap,
 +        char         *swapgname,
 +        char         *splitg0name,
 +        char         *splitg1name,
 +        char         *solgname,
 +        t_blocka     *grps,
 +        char        **gnames)
 +{
 +    int   ig = -1, i = 0, j;
 +    char *splitg;
 +
 +
 +    /* Just a quick check here, more thorough checks are in mdrun */
 +    if (strcmp(splitg0name, splitg1name) == 0)
 +    {
 +        gmx_fatal(FARGS, "The split groups can not both be '%s'.", splitg0name);
 +    }
 +
 +    /* First get the swap group index atoms */
 +    ig        = search_string(swapgname, grps->nr, gnames);
 +    swap->nat = grps->index[ig+1] - grps->index[ig];
 +    if (swap->nat > 0)
 +    {
 +        fprintf(stderr, "Swap group '%s' contains %d atoms.\n", swapgname, swap->nat);
 +        snew(swap->ind, swap->nat);
 +        for (i = 0; i < swap->nat; i++)
 +        {
 +            swap->ind[i] = grps->a[grps->index[ig]+i];
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "You defined an empty group of atoms for swapping.");
 +    }
 +
 +    /* Now do so for the split groups */
 +    for (j = 0; j < 2; j++)
 +    {
 +        if (j == 0)
 +        {
 +            splitg = splitg0name;
 +        }
 +        else
 +        {
 +            splitg = splitg1name;
 +        }
 +
 +        ig                 = search_string(splitg, grps->nr, gnames);
 +        swap->nat_split[j] = grps->index[ig+1] - grps->index[ig];
 +        if (swap->nat_split[j] > 0)
 +        {
 +            fprintf(stderr, "Split group %d '%s' contains %d atom%s.\n",
 +                    j, splitg, swap->nat_split[j], (swap->nat_split[j] > 1) ? "s" : "");
 +            snew(swap->ind_split[j], swap->nat_split[j]);
 +            for (i = 0; i < swap->nat_split[j]; i++)
 +            {
 +                swap->ind_split[j][i] = grps->a[grps->index[ig]+i];
 +            }
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Split group %d has to contain at least 1 atom!", j);
 +        }
 +    }
 +
 +    /* Now get the solvent group index atoms */
 +    ig            = search_string(solgname, grps->nr, gnames);
 +    swap->nat_sol = grps->index[ig+1] - grps->index[ig];
 +    if (swap->nat_sol > 0)
 +    {
 +        fprintf(stderr, "Solvent group '%s' contains %d atoms.\n", solgname, swap->nat_sol);
 +        snew(swap->ind_sol, swap->nat_sol);
 +        for (i = 0; i < swap->nat_sol; i++)
 +        {
 +            swap->ind_sol[i] = grps->a[grps->index[ig]+i];
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "You defined an empty group of solvent. Cannot exchange ions.");
 +    }
 +}
 +
 +
 +void do_index(const char* mdparin, const char *ndx,
 +              gmx_mtop_t *mtop,
 +              gmx_bool bVerbose,
 +              t_inputrec *ir, rvec *v,
 +              warninp_t wi)
 +{
 +    t_blocka     *grps;
 +    gmx_groups_t *groups;
 +    int           natoms;
 +    t_symtab     *symtab;
 +    t_atoms       atoms_all;
 +    char          warnbuf[STRLEN], **gnames;
 +    int           nr, ntcg, ntau_t, nref_t, nacc, nofg, nSA, nSA_points, nSA_time, nSA_temp;
 +    real          tau_min;
 +    int           nstcmin;
 +    int           nacg, nfreeze, nfrdim, nenergy, nvcm, nuser;
 +    char         *ptr1[MAXPTR], *ptr2[MAXPTR], *ptr3[MAXPTR];
 +    int           i, j, k, restnm;
 +    real          SAtime;
 +    gmx_bool      bExcl, bTable, bSetTCpar, bAnneal, bRest;
 +    int           nQMmethod, nQMbasis, nQMcharge, nQMmult, nbSH, nCASorb, nCASelec,
 +                  nSAon, nSAoff, nSAsteps, nQMg, nbOPT, nbTS;
 +    char          warn_buf[STRLEN];
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing index file...\n");
 +    }
 +    debug_gmx();
 +    if (ndx == NULL)
 +    {
 +        snew(grps, 1);
 +        snew(grps->index, 1);
 +        snew(gnames, 1);
 +        atoms_all = gmx_mtop_global_atoms(mtop);
 +        analyse(&atoms_all, grps, &gnames, FALSE, TRUE);
 +        free_t_atoms(&atoms_all, FALSE);
 +    }
 +    else
 +    {
 +        grps = init_index(ndx, &gnames);
 +    }
 +
 +    groups = &mtop->groups;
 +    natoms = mtop->natoms;
 +    symtab = &mtop->symtab;
 +
 +    snew(groups->grpname, grps->nr+1);
 +
 +    for (i = 0; (i < grps->nr); i++)
 +    {
 +        groups->grpname[i] = put_symtab(symtab, gnames[i]);
 +    }
 +    groups->grpname[i] = put_symtab(symtab, "rest");
 +    restnm             = i;
 +    srenew(gnames, grps->nr+1);
 +    gnames[restnm]   = *(groups->grpname[i]);
 +    groups->ngrpname = grps->nr+1;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    ntau_t = str_nelem(is->tau_t, MAXPTR, ptr1);
 +    nref_t = str_nelem(is->ref_t, MAXPTR, ptr2);
 +    ntcg   = str_nelem(is->tcgrps, MAXPTR, ptr3);
 +    if ((ntau_t != ntcg) || (nref_t != ntcg))
 +    {
 +        gmx_fatal(FARGS, "Invalid T coupling input: %d groups, %d ref-t values and "
 +                  "%d tau-t values", ntcg, nref_t, ntau_t);
 +    }
 +
 +    bSetTCpar = (ir->etc || EI_SD(ir->eI) || ir->eI == eiBD || EI_TPI(ir->eI));
 +    do_numbering(natoms, groups, ntcg, ptr3, grps, gnames, egcTC,
 +                 restnm, bSetTCpar ? egrptpALL : egrptpALL_GENREST, bVerbose, wi);
 +    nr            = groups->grps[egcTC].nr;
 +    ir->opts.ngtc = nr;
 +    snew(ir->opts.nrdf, nr);
 +    snew(ir->opts.tau_t, nr);
 +    snew(ir->opts.ref_t, nr);
 +    if (ir->eI == eiBD && ir->bd_fric == 0)
 +    {
 +        fprintf(stderr, "bd-fric=0, so tau-t will be used as the inverse friction constant(s)\n");
 +    }
 +
 +    if (bSetTCpar)
 +    {
 +        if (nr != nref_t)
 +        {
 +            gmx_fatal(FARGS, "Not enough ref-t and tau-t values!");
 +        }
 +
 +        tau_min = 1e20;
 +        for (i = 0; (i < nr); i++)
 +        {
 +            ir->opts.tau_t[i] = strtod(ptr1[i], NULL);
 +            if ((ir->eI == eiBD || ir->eI == eiSD2) && ir->opts.tau_t[i] <= 0)
 +            {
 +                sprintf(warn_buf, "With integrator %s tau-t should be larger than 0", ei_names[ir->eI]);
 +                warning_error(wi, warn_buf);
 +            }
 +
 +            if (ir->etc != etcVRESCALE && ir->opts.tau_t[i] == 0)
 +            {
 +                warning_note(wi, "tau-t = -1 is the value to signal that a group should not have temperature coupling. Treating your use of tau-t = 0 as if you used -1.");
 +            }
 +
 +            if (ir->opts.tau_t[i] >= 0)
 +            {
 +                tau_min = min(tau_min, ir->opts.tau_t[i]);
 +            }
 +        }
 +        if (ir->etc != etcNO && ir->nsttcouple == -1)
 +        {
 +            ir->nsttcouple = ir_optimal_nsttcouple(ir);
 +        }
 +
 +        if (EI_VV(ir->eI))
 +        {
 +            if ((ir->etc == etcNOSEHOOVER) && (ir->epc == epcBERENDSEN))
 +            {
 +                gmx_fatal(FARGS, "Cannot do Nose-Hoover temperature with Berendsen pressure control with md-vv; use either vrescale temperature with berendsen pressure or Nose-Hoover temperature with MTTK pressure");
 +            }
 +            if ((ir->epc == epcMTTK) && (ir->etc > etcNO))
 +            {
 +                if (ir->nstpcouple != ir->nsttcouple)
 +                {
 +                    int mincouple = min(ir->nstpcouple, ir->nsttcouple);
 +                    ir->nstpcouple = ir->nsttcouple = mincouple;
 +                    sprintf(warn_buf, "for current Trotter decomposition methods with vv, nsttcouple and nstpcouple must be equal.  Both have been reset to min(nsttcouple,nstpcouple) = %d", mincouple);
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +        /* velocity verlet with averaged kinetic energy KE = 0.5*(v(t+1/2) - v(t-1/2)) is implemented
 +           primarily for testing purposes, and does not work with temperature coupling other than 1 */
 +
 +        if (ETC_ANDERSEN(ir->etc))
 +        {
 +            if (ir->nsttcouple != 1)
 +            {
 +                ir->nsttcouple = 1;
 +                sprintf(warn_buf, "Andersen temperature control methods assume nsttcouple = 1; there is no need for larger nsttcouple > 1, since no global parameters are computed. nsttcouple has been reset to 1");
 +                warning_note(wi, warn_buf);
 +            }
 +        }
 +        nstcmin = tcouple_min_integration_steps(ir->etc);
 +        if (nstcmin > 1)
 +        {
 +            if (tau_min/(ir->delta_t*ir->nsttcouple) < nstcmin)
 +            {
 +                sprintf(warn_buf, "For proper integration of the %s thermostat, tau-t (%g) should be at least %d times larger than nsttcouple*dt (%g)",
 +                        ETCOUPLTYPE(ir->etc),
 +                        tau_min, nstcmin,
 +                        ir->nsttcouple*ir->delta_t);
 +                warning(wi, warn_buf);
 +            }
 +        }
 +        for (i = 0; (i < nr); i++)
 +        {
 +            ir->opts.ref_t[i] = strtod(ptr2[i], NULL);
 +            if (ir->opts.ref_t[i] < 0)
 +            {
 +                gmx_fatal(FARGS, "ref-t for group %d negative", i);
 +            }
 +        }
 +        /* set the lambda mc temperature to the md integrator temperature (which should be defined
 +           if we are in this conditional) if mc_temp is negative */
 +        if (ir->expandedvals->mc_temp < 0)
 +        {
 +            ir->expandedvals->mc_temp = ir->opts.ref_t[0]; /*for now, set to the first reft */
 +        }
 +    }
 +
 +    /* Simulated annealing for each group. There are nr groups */
 +    nSA = str_nelem(is->anneal, MAXPTR, ptr1);
 +    if (nSA == 1 && (ptr1[0][0] == 'n' || ptr1[0][0] == 'N'))
 +    {
 +        nSA = 0;
 +    }
 +    if (nSA > 0 && nSA != nr)
 +    {
 +        gmx_fatal(FARGS, "Not enough annealing values: %d (for %d groups)\n", nSA, nr);
 +    }
 +    else
 +    {
 +        snew(ir->opts.annealing, nr);
 +        snew(ir->opts.anneal_npoints, nr);
 +        snew(ir->opts.anneal_time, nr);
 +        snew(ir->opts.anneal_temp, nr);
 +        for (i = 0; i < nr; i++)
 +        {
 +            ir->opts.annealing[i]      = eannNO;
 +            ir->opts.anneal_npoints[i] = 0;
 +            ir->opts.anneal_time[i]    = NULL;
 +            ir->opts.anneal_temp[i]    = NULL;
 +        }
 +        if (nSA > 0)
 +        {
 +            bAnneal = FALSE;
 +            for (i = 0; i < nr; i++)
 +            {
 +                if (ptr1[i][0] == 'n' || ptr1[i][0] == 'N')
 +                {
 +                    ir->opts.annealing[i] = eannNO;
 +                }
 +                else if (ptr1[i][0] == 's' || ptr1[i][0] == 'S')
 +                {
 +                    ir->opts.annealing[i] = eannSINGLE;
 +                    bAnneal               = TRUE;
 +                }
 +                else if (ptr1[i][0] == 'p' || ptr1[i][0] == 'P')
 +                {
 +                    ir->opts.annealing[i] = eannPERIODIC;
 +                    bAnneal               = TRUE;
 +                }
 +            }
 +            if (bAnneal)
 +            {
 +                /* Read the other fields too */
 +                nSA_points = str_nelem(is->anneal_npoints, MAXPTR, ptr1);
 +                if (nSA_points != nSA)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-npoints values for %d groups\n", nSA_points, nSA);
 +                }
 +                for (k = 0, i = 0; i < nr; i++)
 +                {
 +                    ir->opts.anneal_npoints[i] = strtol(ptr1[i], NULL, 10);
 +                    if (ir->opts.anneal_npoints[i] == 1)
 +                    {
 +                        gmx_fatal(FARGS, "Please specify at least a start and an end point for annealing\n");
 +                    }
 +                    snew(ir->opts.anneal_time[i], ir->opts.anneal_npoints[i]);
 +                    snew(ir->opts.anneal_temp[i], ir->opts.anneal_npoints[i]);
 +                    k += ir->opts.anneal_npoints[i];
 +                }
 +
 +                nSA_time = str_nelem(is->anneal_time, MAXPTR, ptr1);
 +                if (nSA_time != k)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-time values, wanter %d\n", nSA_time, k);
 +                }
 +                nSA_temp = str_nelem(is->anneal_temp, MAXPTR, ptr2);
 +                if (nSA_temp != k)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-temp values, wanted %d\n", nSA_temp, k);
 +                }
 +
 +                for (i = 0, k = 0; i < nr; i++)
 +                {
 +
 +                    for (j = 0; j < ir->opts.anneal_npoints[i]; j++)
 +                    {
 +                        ir->opts.anneal_time[i][j] = strtod(ptr1[k], NULL);
 +                        ir->opts.anneal_temp[i][j] = strtod(ptr2[k], NULL);
 +                        if (j == 0)
 +                        {
 +                            if (ir->opts.anneal_time[i][0] > (ir->init_t+GMX_REAL_EPS))
 +                            {
 +                                gmx_fatal(FARGS, "First time point for annealing > init_t.\n");
 +                            }
 +                        }
 +                        else
 +                        {
 +                            /* j>0 */
 +                            if (ir->opts.anneal_time[i][j] < ir->opts.anneal_time[i][j-1])
 +                            {
 +                                gmx_fatal(FARGS, "Annealing timepoints out of order: t=%f comes after t=%f\n",
 +                                          ir->opts.anneal_time[i][j], ir->opts.anneal_time[i][j-1]);
 +                            }
 +                        }
 +                        if (ir->opts.anneal_temp[i][j] < 0)
 +                        {
 +                            gmx_fatal(FARGS, "Found negative temperature in annealing: %f\n", ir->opts.anneal_temp[i][j]);
 +                        }
 +                        k++;
 +                    }
 +                }
 +                /* Print out some summary information, to make sure we got it right */
 +                for (i = 0, k = 0; i < nr; i++)
 +                {
 +                    if (ir->opts.annealing[i] != eannNO)
 +                    {
 +                        j = groups->grps[egcTC].nm_ind[i];
 +                        fprintf(stderr, "Simulated annealing for group %s: %s, %d timepoints\n",
 +                                *(groups->grpname[j]), eann_names[ir->opts.annealing[i]],
 +                                ir->opts.anneal_npoints[i]);
 +                        fprintf(stderr, "Time (ps)   Temperature (K)\n");
 +                        /* All terms except the last one */
 +                        for (j = 0; j < (ir->opts.anneal_npoints[i]-1); j++)
 +                        {
 +                            fprintf(stderr, "%9.1f      %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                        }
 +
 +                        /* Finally the last one */
 +                        j = ir->opts.anneal_npoints[i]-1;
 +                        if (ir->opts.annealing[i] == eannSINGLE)
 +                        {
 +                            fprintf(stderr, "%9.1f-     %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "%9.1f      %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                            if (fabs(ir->opts.anneal_temp[i][j]-ir->opts.anneal_temp[i][0]) > GMX_REAL_EPS)
 +                            {
 +                                warning_note(wi, "There is a temperature jump when your annealing loops back.\n");
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        make_pull_groups(ir->pull, is->pull_grp, grps, gnames);
 +
 +        make_pull_coords(ir->pull);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        make_rotation_groups(ir->rot, is->rot_grp, grps, gnames);
 +    }
 +
 +    if (ir->eSwapCoords != eswapNO)
 +    {
 +        make_swap_groups(ir->swap, swapgrp, splitgrp0, splitgrp1, solgrp, grps, gnames);
 +    }
 +
 +    nacc = str_nelem(is->acc, MAXPTR, ptr1);
 +    nacg = str_nelem(is->accgrps, MAXPTR, ptr2);
 +    if (nacg*DIM != nacc)
 +    {
 +        gmx_fatal(FARGS, "Invalid Acceleration input: %d groups and %d acc. values",
 +                  nacg, nacc);
 +    }
 +    do_numbering(natoms, groups, nacg, ptr2, grps, gnames, egcACC,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr = groups->grps[egcACC].nr;
 +    snew(ir->opts.acc, nr);
 +    ir->opts.ngacc = nr;
 +
 +    for (i = k = 0; (i < nacg); i++)
 +    {
 +        for (j = 0; (j < DIM); j++, k++)
 +        {
 +            ir->opts.acc[i][j] = strtod(ptr1[k], NULL);
 +        }
 +    }
 +    for (; (i < nr); i++)
 +    {
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            ir->opts.acc[i][j] = 0;
 +        }
 +    }
 +
 +    nfrdim  = str_nelem(is->frdim, MAXPTR, ptr1);
 +    nfreeze = str_nelem(is->freeze, MAXPTR, ptr2);
 +    if (nfrdim != DIM*nfreeze)
 +    {
 +        gmx_fatal(FARGS, "Invalid Freezing input: %d groups and %d freeze values",
 +                  nfreeze, nfrdim);
 +    }
 +    do_numbering(natoms, groups, nfreeze, ptr2, grps, gnames, egcFREEZE,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr             = groups->grps[egcFREEZE].nr;
 +    ir->opts.ngfrz = nr;
 +    snew(ir->opts.nFreeze, nr);
 +    for (i = k = 0; (i < nfreeze); i++)
 +    {
 +        for (j = 0; (j < DIM); j++, k++)
 +        {
 +            ir->opts.nFreeze[i][j] = (gmx_strncasecmp(ptr1[k], "Y", 1) == 0);
 +            if (!ir->opts.nFreeze[i][j])
 +            {
 +                if (gmx_strncasecmp(ptr1[k], "N", 1) != 0)
 +                {
 +                    sprintf(warnbuf, "Please use Y(ES) or N(O) for freezedim only "
 +                            "(not %s)", ptr1[k]);
 +                    warning(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +    for (; (i < nr); i++)
 +    {
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            ir->opts.nFreeze[i][j] = 0;
 +        }
 +    }
 +
 +    nenergy = str_nelem(is->energy, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nenergy, ptr1, grps, gnames, egcENER,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    add_wall_energrps(groups, ir->nwall, symtab);
 +    ir->opts.ngener = groups->grps[egcENER].nr;
 +    nvcm            = str_nelem(is->vcm, MAXPTR, ptr1);
 +    bRest           =
 +        do_numbering(natoms, groups, nvcm, ptr1, grps, gnames, egcVCM,
 +                     restnm, nvcm == 0 ? egrptpALL_GENREST : egrptpPART, bVerbose, wi);
 +    if (bRest)
 +    {
 +        warning(wi, "Some atoms are not part of any center of mass motion removal group.\n"
 +                "This may lead to artifacts.\n"
 +                "In most cases one should use one group for the whole system.");
 +    }
 +
 +    /* Now we have filled the freeze struct, so we can calculate NRDF */
 +    calc_nrdf(mtop, ir, gnames);
 +
 +    if (v && NULL)
 +    {
 +        real fac, ntot = 0;
 +
 +        /* Must check per group! */
 +        for (i = 0; (i < ir->opts.ngtc); i++)
 +        {
 +            ntot += ir->opts.nrdf[i];
 +        }
 +        if (ntot != (DIM*natoms))
 +        {
 +            fac = sqrt(ntot/(DIM*natoms));
 +            if (bVerbose)
 +            {
 +                fprintf(stderr, "Scaling velocities by a factor of %.3f to account for constraints\n"
 +                        "and removal of center of mass motion\n", fac);
 +            }
 +            for (i = 0; (i < natoms); i++)
 +            {
 +                svmul(fac, v[i], v[i]);
 +            }
 +        }
 +    }
 +
 +    nuser = str_nelem(is->user1, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser1,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nuser = str_nelem(is->user2, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser2,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nuser = str_nelem(is->x_compressed_groups, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcCompressedX,
 +                 restnm, egrptpONE, bVerbose, wi);
 +    nofg = str_nelem(is->orirefitgrp, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nofg, ptr1, grps, gnames, egcORFIT,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +
 +    /* QMMM input processing */
 +    nQMg          = str_nelem(is->QMMM, MAXPTR, ptr1);
 +    nQMmethod     = str_nelem(is->QMmethod, MAXPTR, ptr2);
 +    nQMbasis      = str_nelem(is->QMbasis, MAXPTR, ptr3);
 +    if ((nQMmethod != nQMg) || (nQMbasis != nQMg))
 +    {
 +        gmx_fatal(FARGS, "Invalid QMMM input: %d groups %d basissets"
 +                  " and %d methods\n", nQMg, nQMbasis, nQMmethod);
 +    }
 +    /* group rest, if any, is always MM! */
 +    do_numbering(natoms, groups, nQMg, ptr1, grps, gnames, egcQMMM,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr            = nQMg; /*atoms->grps[egcQMMM].nr;*/
 +    ir->opts.ngQM = nQMg;
 +    snew(ir->opts.QMmethod, nr);
 +    snew(ir->opts.QMbasis, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        /* input consists of strings: RHF CASSCF PM3 .. These need to be
 +         * converted to the corresponding enum in names.c
 +         */
 +        ir->opts.QMmethod[i] = search_QMstring(ptr2[i], eQMmethodNR,
 +                                               eQMmethod_names);
 +        ir->opts.QMbasis[i]  = search_QMstring(ptr3[i], eQMbasisNR,
 +                                               eQMbasis_names);
 +
 +    }
 +    nQMmult   = str_nelem(is->QMmult, MAXPTR, ptr1);
 +    nQMcharge = str_nelem(is->QMcharge, MAXPTR, ptr2);
 +    nbSH      = str_nelem(is->bSH, MAXPTR, ptr3);
 +    snew(ir->opts.QMmult, nr);
 +    snew(ir->opts.QMcharge, nr);
 +    snew(ir->opts.bSH, nr);
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.QMmult[i]   = strtol(ptr1[i], NULL, 10);
 +        ir->opts.QMcharge[i] = strtol(ptr2[i], NULL, 10);
 +        ir->opts.bSH[i]      = (gmx_strncasecmp(ptr3[i], "Y", 1) == 0);
 +    }
 +
 +    nCASelec  = str_nelem(is->CASelectrons, MAXPTR, ptr1);
 +    nCASorb   = str_nelem(is->CASorbitals, MAXPTR, ptr2);
 +    snew(ir->opts.CASelectrons, nr);
 +    snew(ir->opts.CASorbitals, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.CASelectrons[i] = strtol(ptr1[i], NULL, 10);
 +        ir->opts.CASorbitals[i]  = strtol(ptr2[i], NULL, 10);
 +    }
 +    /* special optimization options */
 +
 +    nbOPT = str_nelem(is->bOPT, MAXPTR, ptr1);
 +    nbTS  = str_nelem(is->bTS, MAXPTR, ptr2);
 +    snew(ir->opts.bOPT, nr);
 +    snew(ir->opts.bTS, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.bOPT[i] = (gmx_strncasecmp(ptr1[i], "Y", 1) == 0);
 +        ir->opts.bTS[i]  = (gmx_strncasecmp(ptr2[i], "Y", 1) == 0);
 +    }
 +    nSAon     = str_nelem(is->SAon, MAXPTR, ptr1);
 +    nSAoff    = str_nelem(is->SAoff, MAXPTR, ptr2);
 +    nSAsteps  = str_nelem(is->SAsteps, MAXPTR, ptr3);
 +    snew(ir->opts.SAon, nr);
 +    snew(ir->opts.SAoff, nr);
 +    snew(ir->opts.SAsteps, nr);
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.SAon[i]    = strtod(ptr1[i], NULL);
 +        ir->opts.SAoff[i]   = strtod(ptr2[i], NULL);
 +        ir->opts.SAsteps[i] = strtol(ptr3[i], NULL, 10);
 +    }
 +    /* end of QMMM input */
 +
 +    if (bVerbose)
 +    {
 +        for (i = 0; (i < egcNR); i++)
 +        {
 +            fprintf(stderr, "%-16s has %d element(s):", gtypes[i], groups->grps[i].nr);
 +            for (j = 0; (j < groups->grps[i].nr); j++)
 +            {
 +                fprintf(stderr, " %s", *(groups->grpname[groups->grps[i].nm_ind[j]]));
 +            }
 +            fprintf(stderr, "\n");
 +        }
 +    }
 +
 +    nr = groups->grps[egcENER].nr;
 +    snew(ir->opts.egp_flags, nr*nr);
 +
 +    bExcl = do_egp_flag(ir, groups, "energygrp-excl", is->egpexcl, EGP_EXCL);
 +    if (bExcl && ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        warning_error(wi, "Energy group exclusions are not (yet) implemented for the Verlet scheme");
 +    }
 +    if (bExcl && EEL_FULL(ir->coulombtype))
 +    {
 +        warning(wi, "Can not exclude the lattice Coulomb energy between energy groups");
 +    }
 +
 +    bTable = do_egp_flag(ir, groups, "energygrp-table", is->egptable, EGP_TABLE);
 +    if (bTable && !(ir->vdwtype == evdwUSER) &&
 +        !(ir->coulombtype == eelUSER) && !(ir->coulombtype == eelPMEUSER) &&
 +        !(ir->coulombtype == eelPMEUSERSWITCH))
 +    {
 +        gmx_fatal(FARGS, "Can only have energy group pair tables in combination with user tables for VdW and/or Coulomb");
 +    }
 +
 +    decode_cos(is->efield_x, &(ir->ex[XX]));
 +    decode_cos(is->efield_xt, &(ir->et[XX]));
 +    decode_cos(is->efield_y, &(ir->ex[YY]));
 +    decode_cos(is->efield_yt, &(ir->et[YY]));
 +    decode_cos(is->efield_z, &(ir->ex[ZZ]));
 +    decode_cos(is->efield_zt, &(ir->et[ZZ]));
 +
 +    if (ir->bAdress)
 +    {
 +        do_adress_index(ir->adress, groups, gnames, &(ir->opts), wi);
 +    }
 +
 +    for (i = 0; (i < grps->nr); i++)
 +    {
 +        sfree(gnames[i]);
 +    }
 +    sfree(gnames);
 +    done_blocka(grps);
 +    sfree(grps);
 +
 +}
 +
 +
 +
 +static void check_disre(gmx_mtop_t *mtop)
 +{
 +    gmx_ffparams_t *ffparams;
 +    t_functype     *functype;
 +    t_iparams      *ip;
 +    int             i, ndouble, ftype;
 +    int             label, old_label;
 +
 +    if (gmx_mtop_ftype_count(mtop, F_DISRES) > 0)
 +    {
 +        ffparams  = &mtop->ffparams;
 +        functype  = ffparams->functype;
 +        ip        = ffparams->iparams;
 +        ndouble   = 0;
 +        old_label = -1;
 +        for (i = 0; i < ffparams->ntypes; i++)
 +        {
 +            ftype = functype[i];
 +            if (ftype == F_DISRES)
 +            {
 +                label = ip[i].disres.label;
 +                if (label == old_label)
 +                {
 +                    fprintf(stderr, "Distance restraint index %d occurs twice\n", label);
 +                    ndouble++;
 +                }
 +                old_label = label;
 +            }
 +        }
 +        if (ndouble > 0)
 +        {
 +            gmx_fatal(FARGS, "Found %d double distance restraint indices,\n"
 +                      "probably the parameters for multiple pairs in one restraint "
 +                      "are not identical\n", ndouble);
 +        }
 +    }
 +}
 +
 +static gmx_bool absolute_reference(t_inputrec *ir, gmx_mtop_t *sys,
 +                                   gmx_bool posres_only,
 +                                   ivec AbsRef)
 +{
 +    int                  d, g, i;
 +    gmx_mtop_ilistloop_t iloop;
 +    t_ilist             *ilist;
 +    int                  nmol;
 +    t_iparams           *pr;
 +
 +    clear_ivec(AbsRef);
 +
 +    if (!posres_only)
 +    {
 +        /* Check the COM */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            AbsRef[d] = (d < ndof_com(ir) ? 0 : 1);
 +        }
 +        /* Check for freeze groups */
 +        for (g = 0; g < ir->opts.ngfrz; g++)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (ir->opts.nFreeze[g][d] != 0)
 +                {
 +                    AbsRef[d] = 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Check for position restraints */
 +    iloop = gmx_mtop_ilistloop_init(sys);
 +    while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol))
 +    {
 +        if (nmol > 0 &&
 +            (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0))
 +        {
 +            for (i = 0; i < ilist[F_POSRES].nr; i += 2)
 +            {
 +                pr = &sys->ffparams.iparams[ilist[F_POSRES].iatoms[i]];
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    if (pr->posres.fcA[d] != 0)
 +                    {
 +                        AbsRef[d] = 1;
 +                    }
 +                }
 +            }
 +            for (i = 0; i < ilist[F_FBPOSRES].nr; i += 2)
 +            {
 +                /* Check for flat-bottom posres */
 +                pr = &sys->ffparams.iparams[ilist[F_FBPOSRES].iatoms[i]];
 +                if (pr->fbposres.k != 0)
 +                {
 +                    switch (pr->fbposres.geom)
 +                    {
 +                        case efbposresSPHERE:
 +                            AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1;
 +                            break;
 +                        case efbposresCYLINDER:
 +                            AbsRef[XX] = AbsRef[YY] = 1;
 +                            break;
 +                        case efbposresX: /* d=XX */
 +                        case efbposresY: /* d=YY */
 +                        case efbposresZ: /* d=ZZ */
 +                            d         = pr->fbposres.geom - efbposresX;
 +                            AbsRef[d] = 1;
 +                            break;
 +                        default:
 +                            gmx_fatal(FARGS, " Invalid geometry for flat-bottom position restraint.\n"
 +                                      "Expected nr between 1 and %d. Found %d\n", efbposresNR-1,
 +                                      pr->fbposres.geom);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0);
 +}
 +
 +static void
 +check_combination_rule_differences(const gmx_mtop_t *mtop, int state,
 +                                   gmx_bool *bC6ParametersWorkWithGeometricRules,
 +                                   gmx_bool *bC6ParametersWorkWithLBRules,
 +                                   gmx_bool *bLBRulesPossible)
 +{
 +    int           ntypes, tpi, tpj, thisLBdiff, thisgeomdiff;
 +    int          *typecount;
 +    real          tol;
 +    double        geometricdiff, LBdiff;
 +    double        c6i, c6j, c12i, c12j;
 +    double        c6, c6_geometric, c6_LB;
 +    double        sigmai, sigmaj, epsi, epsj;
 +    gmx_bool      bCanDoLBRules, bCanDoGeometricRules;
 +    const char   *ptr;
 +
 +    /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed)
 +     * force-field floating point parameters.
 +     */
 +    tol = 1e-5;
 +    ptr = getenv("GMX_LJCOMB_TOL");
 +    if (ptr != NULL)
 +    {
 +        double dbl;
 +
 +        sscanf(ptr, "%lf", &dbl);
 +        tol = dbl;
 +    }
 +
 +    *bC6ParametersWorkWithLBRules         = TRUE;
 +    *bC6ParametersWorkWithGeometricRules  = TRUE;
 +    bCanDoLBRules                         = TRUE;
 +    bCanDoGeometricRules                  = TRUE;
 +    ntypes                                = mtop->ffparams.atnr;
 +    snew(typecount, ntypes);
 +    gmx_mtop_count_atomtypes(mtop, state, typecount);
 +    geometricdiff           = LBdiff = 0.0;
 +    *bLBRulesPossible       = TRUE;
 +    for (tpi = 0; tpi < ntypes; ++tpi)
 +    {
 +        c6i  = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c6;
 +        c12i = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c12;
 +        for (tpj = tpi; tpj < ntypes; ++tpj)
 +        {
 +            c6j          = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c6;
 +            c12j         = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c12;
 +            c6           = mtop->ffparams.iparams[ntypes * tpi + tpj].lj.c6;
 +            c6_geometric = sqrt(c6i * c6j);
 +            if (!gmx_numzero(c6_geometric))
 +            {
 +                if (!gmx_numzero(c12i) && !gmx_numzero(c12j))
 +                {
 +                    sigmai   = pow(c12i / c6i, 1.0/6.0);
 +                    sigmaj   = pow(c12j / c6j, 1.0/6.0);
 +                    epsi     = c6i * c6i /(4.0 * c12i);
 +                    epsj     = c6j * c6j /(4.0 * c12j);
 +                    c6_LB    = 4.0 * pow(epsi * epsj, 1.0/2.0) * pow(0.5 * (sigmai + sigmaj), 6);
 +                }
 +                else
 +                {
 +                    *bLBRulesPossible = FALSE;
 +                    c6_LB             = c6_geometric;
 +                }
 +                bCanDoLBRules = gmx_within_tol(c6_LB, c6, tol);
 +            }
 +
 +            if (FALSE == bCanDoLBRules)
 +            {
 +                *bC6ParametersWorkWithLBRules = FALSE;
 +            }
 +
 +            bCanDoGeometricRules = gmx_within_tol(c6_geometric, c6, tol);
 +
 +            if (FALSE == bCanDoGeometricRules)
 +            {
 +                *bC6ParametersWorkWithGeometricRules = FALSE;
 +            }
 +        }
 +    }
 +    sfree(typecount);
 +}
 +
 +static void
 +check_combination_rules(const t_inputrec *ir, const gmx_mtop_t *mtop,
 +                        warninp_t wi)
 +{
 +    char     err_buf[256];
 +    gmx_bool bLBRulesPossible, bC6ParametersWorkWithGeometricRules, bC6ParametersWorkWithLBRules;
 +
 +    check_combination_rule_differences(mtop, 0,
 +                                       &bC6ParametersWorkWithGeometricRules,
 +                                       &bC6ParametersWorkWithLBRules,
 +                                       &bLBRulesPossible);
 +    if (ir->ljpme_combination_rule == eljpmeLB)
 +    {
 +        if (FALSE == bC6ParametersWorkWithLBRules || FALSE == bLBRulesPossible)
 +        {
 +            warning(wi, "You are using arithmetic-geometric combination rules "
 +                    "in LJ-PME, but your non-bonded C6 parameters do not "
 +                    "follow these rules.");
 +        }
 +    }
 +    else
 +    {
 +        if (FALSE == bC6ParametersWorkWithGeometricRules)
 +        {
 +            if (ir->eDispCorr != edispcNO)
 +            {
 +                warning_note(wi, "You are using geometric combination rules in "
 +                             "LJ-PME, but your non-bonded C6 parameters do "
 +                             "not follow these rules. "
 +                             "This will introduce very small errors in the forces and energies in "
 +                             "your simulations. Dispersion correction will correct total energy "
 +                             "and/or pressure for isotropic systems, but not forces or surface tensions.");
 +            }
 +            else
 +            {
 +                warning_note(wi, "You are using geometric combination rules in "
 +                             "LJ-PME, but your non-bonded C6 parameters do "
 +                             "not follow these rules. "
 +                             "This will introduce very small errors in the forces and energies in "
 +                             "your simulations. If your system is homogeneous, consider using dispersion correction "
 +                             "for the total energy and pressure.");
 +            }
 +        }
 +    }
 +}
 +
 +void triple_check(const char *mdparin, t_inputrec *ir, gmx_mtop_t *sys,
 +                  warninp_t wi)
 +{
 +    char                      err_buf[256];
 +    int                       i, m, c, nmol, npct;
 +    gmx_bool                  bCharge, bAcc;
 +    real                      gdt_max, *mgrp, mt;
 +    rvec                      acc;
 +    gmx_mtop_atomloop_block_t aloopb;
 +    gmx_mtop_atomloop_all_t   aloop;
 +    t_atom                   *atom;
 +    ivec                      AbsRef;
 +    char                      warn_buf[STRLEN];
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != eiBD &&
 +        ir->comm_mode == ecmNO &&
 +        !(absolute_reference(ir, sys, FALSE, AbsRef) || ir->nsteps <= 10) &&
 +        !ETC_ANDERSEN(ir->etc))
 +    {
 +        warning(wi, "You are not using center of mass motion removal (mdp option comm-mode), numerical rounding errors can lead to build up of kinetic energy of the center of mass");
 +    }
 +
 +    /* Check for pressure coupling with absolute position restraints */
 +    if (ir->epc != epcNO && ir->refcoord_scaling == erscNO)
 +    {
 +        absolute_reference(ir, sys, TRUE, AbsRef);
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                if (AbsRef[m] && norm2(ir->compress[m]) > 0)
 +                {
 +                    warning(wi, "You are using pressure coupling with absolute position restraints, this will give artifacts. Use the refcoord_scaling option.");
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    bCharge = FALSE;
 +    aloopb  = gmx_mtop_atomloop_block_init(sys);
 +    while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol))
 +    {
 +        if (atom->q != 0 || atom->qB != 0)
 +        {
 +            bCharge = TRUE;
 +        }
 +    }
 +
 +    if (!bCharge)
 +    {
 +        if (EEL_FULL(ir->coulombtype))
 +        {
 +            sprintf(err_buf,
 +                    "You are using full electrostatics treatment %s for a system without charges.\n"
 +                    "This costs a lot of performance for just processing zeros, consider using %s instead.\n",
 +                    EELTYPE(ir->coulombtype), EELTYPE(eelCUT));
 +            warning(wi, err_buf);
 +        }
 +    }
 +    else
 +    {
 +        if (ir->coulombtype == eelCUT && ir->rcoulomb > 0 && !ir->implicit_solvent)
 +        {
 +            sprintf(err_buf,
 +                    "You are using a plain Coulomb cut-off, which might produce artifacts.\n"
 +                    "You might want to consider using %s electrostatics.\n",
 +                    EELTYPE(eelPME));
 +            warning_note(wi, err_buf);
 +        }
 +    }
 +
 +    /* Check if combination rules used in LJ-PME are the same as in the force field */
 +    if (EVDW_PME(ir->vdwtype))
 +    {
 +        check_combination_rules(ir, sys, wi);
 +    }
 +
 +    /* Generalized reaction field */
 +    if (ir->opts.ngtc == 0)
 +    {
 +        sprintf(err_buf, "No temperature coupling while using coulombtype %s",
 +                eel_names[eelGRF]);
 +        CHECK(ir->coulombtype == eelGRF);
 +    }
 +    else
 +    {
 +        sprintf(err_buf, "When using coulombtype = %s"
 +                " ref-t for temperature coupling should be > 0",
 +                eel_names[eelGRF]);
 +        CHECK((ir->coulombtype == eelGRF) && (ir->opts.ref_t[0] <= 0));
 +    }
 +
 +    if (ir->eI == eiSD1 &&
 +        (gmx_mtop_ftype_count(sys, F_CONSTR) > 0 ||
 +         gmx_mtop_ftype_count(sys, F_SETTLE) > 0))
 +    {
 +        sprintf(warn_buf, "With constraints integrator %s is less accurate, consider using %s instead", ei_names[ir->eI], ei_names[eiSD2]);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    bAcc = FALSE;
 +    for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +    {
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fabs(ir->opts.acc[i][m]) > 1e-6)
 +            {
 +                bAcc = TRUE;
 +            }
 +        }
 +    }
 +    if (bAcc)
 +    {
 +        clear_rvec(acc);
 +        snew(mgrp, sys->groups.grps[egcACC].nr);
 +        aloop = gmx_mtop_atomloop_all_init(sys);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            mgrp[ggrpnr(&sys->groups, egcACC, i)] += atom->m;
 +        }
 +        mt = 0.0;
 +        for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                acc[m] += ir->opts.acc[i][m]*mgrp[i];
 +            }
 +            mt += mgrp[i];
 +        }
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fabs(acc[m]) > 1e-6)
 +            {
 +                const char *dim[DIM] = { "X", "Y", "Z" };
 +                fprintf(stderr,
 +                        "Net Acceleration in %s direction, will %s be corrected\n",
 +                        dim[m], ir->nstcomm != 0 ? "" : "not");
 +                if (ir->nstcomm != 0 && m < ndof_com(ir))
 +                {
 +                    acc[m] /= mt;
 +                    for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +                    {
 +                        ir->opts.acc[i][m] -= acc[m];
 +                    }
 +                }
 +            }
 +        }
 +        sfree(mgrp);
 +    }
 +
 +    if (ir->efep != efepNO && ir->fepvals->sc_alpha != 0 &&
 +        !gmx_within_tol(sys->ffparams.reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +    {
 +        gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12");
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        gmx_bool bPullAbsoluteRef;
 +
 +        bPullAbsoluteRef = FALSE;
 +        for (i = 0; i < ir->pull->ncoord; i++)
 +        {
 +            bPullAbsoluteRef = bPullAbsoluteRef ||
 +                ir->pull->coord[i].group[0] == 0 ||
 +                ir->pull->coord[i].group[1] == 0;
 +        }
 +        if (bPullAbsoluteRef)
 +        {
 +            absolute_reference(ir, sys, FALSE, AbsRef);
 +            for (m = 0; m < DIM; m++)
 +            {
 +                if (ir->pull->dim[m] && !AbsRef[m])
 +                {
 +                    warning(wi, "You are using an absolute reference for pulling, but the rest of the system does not have an absolute reference. This will lead to artifacts.");
 +                    break;
 +                }
 +            }
 +        }
 +
 +        if (ir->pull->eGeom == epullgDIRPBC)
 +        {
 +            for (i = 0; i < 3; i++)
 +            {
 +                for (m = 0; m <= i; m++)
 +                {
 +                    if ((ir->epc != epcNO && ir->compress[i][m] != 0) ||
 +                        ir->deform[i][m] != 0)
 +                    {
 +                        for (c = 0; c < ir->pull->ncoord; c++)
 +                        {
 +                            if (ir->pull->coord[c].vec[m] != 0)
 +                            {
 +                                gmx_fatal(FARGS, "Can not have dynamic box while using pull geometry '%s' (dim %c)", EPULLGEOM(ir->pull->eGeom), 'x'+m);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    check_disre(sys);
 +}
 +
 +void double_check(t_inputrec *ir, matrix box, gmx_bool bConstr, warninp_t wi)
 +{
 +    real        min_size;
 +    gmx_bool    bTWIN;
 +    char        warn_buf[STRLEN];
 +    const char *ptr;
 +
 +    ptr = check_box(ir->ePBC, box);
 +    if (ptr)
 +    {
 +        warning_error(wi, ptr);
 +    }
 +
 +    if (bConstr && ir->eConstrAlg == econtSHAKE)
 +    {
 +        if (ir->shake_tol <= 0.0)
 +        {
 +            sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n",
 +                    ir->shake_tol);
 +            warning_error(wi, warn_buf);
 +        }
 +
 +        if (IR_TWINRANGE(*ir) && ir->nstlist > 1)
 +        {
 +            sprintf(warn_buf, "With twin-range cut-off's and SHAKE the virial and the pressure are incorrect.");
 +            if (ir->epc == epcNO)
 +            {
 +                warning(wi, warn_buf);
 +            }
 +            else
 +            {
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +    }
 +
 +    if ( (ir->eConstrAlg == econtLINCS) && bConstr)
 +    {
 +        /* If we have Lincs constraints: */
 +        if (ir->eI == eiMD && ir->etc == etcNO &&
 +            ir->eConstrAlg == econtLINCS && ir->nLincsIter == 1)
 +        {
 +            sprintf(warn_buf, "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n");
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        if ((ir->eI == eiCG || ir->eI == eiLBFGS) && (ir->nProjOrder < 8))
 +        {
 +            sprintf(warn_buf, "For accurate %s with LINCS constraints, lincs-order should be 8 or more.", ei_names[ir->eI]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir->epc == epcMTTK)
 +        {
 +            warning_error(wi, "MTTK not compatible with lincs -- use shake instead.");
 +        }
 +    }
 +
++    if (bConstr && ir->epc == epcMTTK)
++    {
++        warning_note(wi, "MTTK with constraints is deprecated, and will be removed in GROMACS 5.1");
++    }
++
 +    if (ir->LincsWarnAngle > 90.0)
 +    {
 +        sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n");
 +        warning(wi, warn_buf);
 +        ir->LincsWarnAngle = 90.0;
 +    }
 +
 +    if (ir->ePBC != epbcNONE)
 +    {
 +        if (ir->nstlist == 0)
 +        {
 +            warning(wi, "With nstlist=0 atoms are only put into the box at step 0, therefore drifting atoms might cause the simulation to crash.");
 +        }
 +        bTWIN = (ir->rlistlong > ir->rlist);
 +        if (ir->ns_type == ensGRID)
 +        {
 +            if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box))
 +            {
 +                sprintf(warn_buf, "ERROR: The cut-off length is longer than half the shortest box vector or longer than the smallest box diagonal element. Increase the box size or decrease %s.\n",
 +                        bTWIN ? (ir->rcoulomb == ir->rlistlong ? "rcoulomb" : "rvdw") : "rlist");
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +        else
 +        {
 +            min_size = min(box[XX][XX], min(box[YY][YY], box[ZZ][ZZ]));
 +            if (2*ir->rlistlong >= min_size)
 +            {
 +                sprintf(warn_buf, "ERROR: One of the box lengths is smaller than twice the cut-off length. Increase the box size or decrease rlist.");
 +                warning_error(wi, warn_buf);
 +                if (TRICLINIC(box))
 +                {
 +                    fprintf(stderr, "Grid search might allow larger cut-off's than simple search with triclinic boxes.");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void check_chargegroup_radii(const gmx_mtop_t *mtop, const t_inputrec *ir,
 +                             rvec *x,
 +                             warninp_t wi)
 +{
 +    real rvdw1, rvdw2, rcoul1, rcoul2;
 +    char warn_buf[STRLEN];
 +
 +    calc_chargegroup_radii(mtop, x, &rvdw1, &rvdw2, &rcoul1, &rcoul2);
 +
 +    if (rvdw1 > 0)
 +    {
 +        printf("Largest charge group radii for Van der Waals: %5.3f, %5.3f nm\n",
 +               rvdw1, rvdw2);
 +    }
 +    if (rcoul1 > 0)
 +    {
 +        printf("Largest charge group radii for Coulomb:       %5.3f, %5.3f nm\n",
 +               rcoul1, rcoul2);
 +    }
 +
 +    if (ir->rlist > 0)
 +    {
 +        if (rvdw1  + rvdw2  > ir->rlist ||
 +            rcoul1 + rcoul2 > ir->rlist)
 +        {
 +            sprintf(warn_buf,
 +                    "The sum of the two largest charge group radii (%f) "
 +                    "is larger than rlist (%f)\n",
 +                    max(rvdw1+rvdw2, rcoul1+rcoul2), ir->rlist);
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            /* Here we do not use the zero at cut-off macro,
 +             * since user defined interactions might purposely
 +             * not be zero at the cut-off.
 +             */
 +            if (ir_vdw_is_zero_at_cutoff(ir) &&
 +                rvdw1 + rvdw2 > ir->rlistlong - ir->rvdw)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group "
 +                        "radii (%f) is larger than %s (%f) - rvdw (%f).\n"
 +                        "With exact cut-offs, better performance can be "
 +                        "obtained with cutoff-scheme = %s, because it "
 +                        "does not use charge groups at all.",
 +                        rvdw1+rvdw2,
 +                        ir->rlistlong > ir->rlist ? "rlistlong" : "rlist",
 +                        ir->rlistlong, ir->rvdw,
 +                        ecutscheme_names[ecutsVERLET]);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +            if (ir_coulomb_is_zero_at_cutoff(ir) &&
 +                rcoul1 + rcoul2 > ir->rlistlong - ir->rcoulomb)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than %s (%f) - rcoulomb (%f).\n"
 +                        "With exact cut-offs, better performance can be obtained with cutoff-scheme = %s, because it does not use charge groups at all.",
 +                        rcoul1+rcoul2,
 +                        ir->rlistlong > ir->rlist ? "rlistlong" : "rlist",
 +                        ir->rlistlong, ir->rcoulomb,
 +                        ecutscheme_names[ecutsVERLET]);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +}
index 6a0fb7c7cabaa88290f8931986fa7270a2280b44,0000000000000000000000000000000000000000..efeef40614522c918880397a85b0a4b1697f591e
mode 100644,000000..100644
--- /dev/null
@@@ -1,332 -1,0 +1,332 @@@
-                            rvec *x0, rvec *x1);
- /* Move x0 and also x1 if x1!=NULL */
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2005,2006,2007,2008,2009,2010,2012,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.
 + */
 +
 +#ifndef _domdec_h
 +#define _domdec_h
 +
 +#include "typedefs.h"
 +#include "vsite.h"
 +#include "genborn.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +int ddglatnr(gmx_domdec_t *dd, int i);
 +/* Returns the global topology atom number belonging to local atom index i.
 + * This function is intended for writing ascii output
 + * and returns atom numbers starting at 1.
 + * When dd=NULL returns i+1.
 + */
 +
 +t_block *dd_charge_groups_global(gmx_domdec_t *dd);
 +/* Return a block struct for the charge groups of the whole system */
 +
 +gmx_bool dd_filled_nsgrid_home(gmx_domdec_t *dd);
 +/* Is the ns grid already filled with the home particles? */
 +
 +void dd_store_state(gmx_domdec_t *dd, t_state *state);
 +/* Store the global cg indices of the home cgs in state,
 + * so it can be reset, even after a new DD partitioning.
 + */
 +
 +gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd);
 +
 +void dd_get_ns_ranges(gmx_domdec_t *dd, int icg,
 +                      int *jcg0, int *jcg1, ivec shift0, ivec shift1);
 +
 +int dd_natoms_vsite(gmx_domdec_t *dd);
 +
 +void dd_get_constraint_range(gmx_domdec_t *dd,
 +                             int *at_start, int *at_end);
 +
 +real dd_cutoff_mbody(gmx_domdec_t *dd);
 +
 +real dd_cutoff_twobody(gmx_domdec_t *dd);
 +
 +void get_pme_nnodes(const gmx_domdec_t *dd,
 +                    int *npmenodes_x, int *npmenodes_y);
 +/* Get the number of PME nodes along x and y, can be called with dd=NULL */
 +
 +gmx_bool gmx_pmeonlynode(t_commrec *cr, int nodeid);
 +/* Return if nodeid in cr->mpi_comm_mysim is a PME-only node */
 +
 +void get_pme_ddnodes(t_commrec *cr, int pmenodeid,
 +                     int *nmy_ddnodes, int **my_ddnodes, int *node_peer);
 +/* Returns the set of DD nodes that communicate with pme node cr->nodeid */
 +
 +int dd_pme_maxshift_x(gmx_domdec_t *dd);
 +/* Returns the maximum shift for coordinate communication in PME, dim x */
 +
 +int dd_pme_maxshift_y(gmx_domdec_t *dd);
 +/* Returns the maximum shift for coordinate communication in PME, dim y */
 +
 +void make_dd_communicators(FILE *fplog, t_commrec *cr, int dd_node_order);
 +
 +gmx_domdec_t *
 +init_domain_decomposition(FILE *fplog,
 +                          t_commrec *cr,
 +                          unsigned long Flags,
 +                          ivec nc,
 +                          real comm_distance_min, real rconstr,
 +                          const char *dlb_opt, real dlb_scale,
 +                          const char *sizex, const char *sizey, const char *sizez,
 +                          gmx_mtop_t *mtop, t_inputrec *ir,
 +                          matrix box, rvec *x,
 +                          gmx_ddbox_t *ddbox,
 +                          int *npme_x, int *npme_y);
 +
 +void dd_init_bondeds(FILE *fplog,
 +                     gmx_domdec_t *dd, gmx_mtop_t *mtop,
 +                     gmx_vsite_t *vsite,
 +                     t_inputrec *ir, gmx_bool bBCheck, cginfo_mb_t *cginfo_mb);
 +/* Initialize data structures for bonded interactions */
 +
 +gmx_bool dd_bonded_molpbc(gmx_domdec_t *dd, int ePBC);
 +/* Returns if we need to do pbc for calculating bonded interactions */
 +
 +void set_dd_parameters(FILE *fplog, gmx_domdec_t *dd, real dlb_scale,
 +                       t_inputrec *ir,
 +                       gmx_ddbox_t *ddbox);
 +/* Set DD grid dimensions and limits,
 + * should be called after calling dd_init_bondeds.
 + */
 +
 +gmx_bool change_dd_cutoff(t_commrec *cr, t_state *state, t_inputrec *ir,
 +                          real cutoff_req );
 +/* Change the DD non-bonded communication cut-off.
 + * This could fail when trying to increase the cut-off,
 + * then FALSE will be returned and the cut-off is not modified.
 + */
 +
 +void change_dd_dlb_cutoff_limit(t_commrec *cr);
 +/* Domain boundary changes due to the DD dynamic load balancing can limit
 + * the cut-off distance that can be set in change_dd_cutoff. This function
 + * limits the DLB such that using the currently set cut-off should still be
 + * possible after subsequently setting a shorter cut-off with change_dd_cutoff.
 + */
 +
 +void dd_setup_dlb_resource_sharing(t_commrec           *cr,
 +                                   const gmx_hw_info_t *hwinfo,
 +                                   const gmx_hw_opt_t  *hw_opt);
 +/* When domains (PP MPI ranks) share a GPU, the individual GPU wait times
 + * are meaningless, as it depends on the order in which tasks on the same
 + * GPU finish. Therefore there wait times need to be averaged over the ranks
 + * sharing the same GPU. This function sets up the communication for that.
 + */
 +
 +void setup_dd_grid(FILE *fplog, gmx_domdec_t *dd);
 +
 +void dd_collect_vec(gmx_domdec_t *dd,
 +                    t_state *state_local, rvec *lv, rvec *v);
 +
 +void dd_collect_state(gmx_domdec_t *dd,
 +                      t_state *state_local, t_state *state);
 +
 +enum {
 +    ddCyclStep, ddCyclPPduringPME, ddCyclF, ddCyclWaitGPU, ddCyclPME, ddCyclNr
 +};
 +
 +void dd_cycles_add(gmx_domdec_t *dd, float cycles, int ddCycl);
 +/* Add the wallcycle count to the DD counter */
 +
 +void dd_force_flop_start(gmx_domdec_t *dd, t_nrnb *nrnb);
 +/* Start the force flop count */
 +
 +void dd_force_flop_stop(gmx_domdec_t *dd, t_nrnb *nrnb);
 +/* Stop the force flop count */
 +
 +float dd_pme_f_ratio(gmx_domdec_t *dd);
 +/* Return the PME/PP force load ratio, or -1 if nothing was measured.
 + * Should only be called on the DD master node.
 + */
 +
 +void dd_move_x(gmx_domdec_t *dd, matrix box, rvec x[]);
 +/* Communicate the coordinates to the neighboring cells and do pbc. */
 +
 +void dd_move_f(gmx_domdec_t *dd, rvec f[], rvec *fshift);
 +/* Sum the forces over the neighboring cells.
 + * When fshift!=NULL the shift forces are updated to obtain
 + * the correct virial from the single sum including f.
 + */
 +
 +void dd_atom_spread_real(gmx_domdec_t *dd, real v[]);
 +/* Communicate a real for each atom to the neighboring cells. */
 +
 +void dd_atom_sum_real(gmx_domdec_t *dd, real v[]);
 +/* Sum the contributions to a real for each atom over the neighboring cells. */
 +
 +void dd_partition_system(FILE                *fplog,
 +                         gmx_int64_t          step,
 +                         t_commrec           *cr,
 +                         gmx_bool             bMasterState,
 +                         int                  nstglobalcomm,
 +                         t_state             *state_global,
 +                         gmx_mtop_t          *top_global,
 +                         t_inputrec          *ir,
 +                         t_state             *state_local,
 +                         rvec               **f,
 +                         t_mdatoms           *mdatoms,
 +                         gmx_localtop_t      *top_local,
 +                         t_forcerec          *fr,
 +                         gmx_vsite_t         *vsite,
 +                         gmx_shellfc_t        shellfc,
 +                         gmx_constr_t         constr,
 +                         t_nrnb              *nrnb,
 +                         gmx_wallcycle_t      wcycle,
 +                         gmx_bool             bVerbose);
 +/* Partition the system over the nodes.
 + * step is only used for printing error messages.
 + * If bMasterState==TRUE then state_global from the master node is used,
 + * else state_local is redistributed between the nodes.
 + * When f!=NULL, *f will be reallocated to the size of state_local.
 + */
 +
 +void reset_dd_statistics_counters(gmx_domdec_t *dd);
 +/* Reset all the statistics and counters for total run counting */
 +
 +void print_dd_statistics(t_commrec *cr, t_inputrec *ir, FILE *fplog);
 +
 +/* In domdec_con.c */
 +
 +void dd_move_f_vsites(gmx_domdec_t *dd, rvec *f, rvec *fshift);
 +
 +void dd_clear_f_vsites(gmx_domdec_t *dd, rvec *f);
 +
 +void dd_move_x_constraints(gmx_domdec_t *dd, matrix box,
++                           rvec *x0, rvec *x1, gmx_bool bX1IsCoord);
++/* Move x0 and also x1 if x1!=NULL. bX1IsCoord tells if to do PBC on x1 */
 +
 +void dd_move_x_vsites(gmx_domdec_t *dd, matrix box, rvec *x);
 +
 +int *dd_constraints_nlocalatoms(gmx_domdec_t *dd);
 +
 +void dd_clear_local_constraint_indices(gmx_domdec_t *dd);
 +
 +void dd_clear_local_vsite_indices(gmx_domdec_t *dd);
 +
 +int dd_make_local_vsites(gmx_domdec_t *dd, int at_start, t_ilist *lil);
 +
 +int dd_make_local_constraints(gmx_domdec_t *dd, int at_start,
 +                              const gmx_mtop_t *mtop,
 +                              const int *cginfo,
 +                              gmx_constr_t constr, int nrec,
 +                              t_ilist *il_local);
 +
 +void init_domdec_constraints(gmx_domdec_t *dd,
 +                             gmx_mtop_t   *mtop);
 +
 +void init_domdec_vsites(gmx_domdec_t *dd, int n_intercg_vsite);
 +
 +
 +/* In domdec_top.c */
 +
 +void dd_print_missing_interactions(FILE *fplog, t_commrec *cr,
 +                                   int local_count,  gmx_mtop_t *top_global, t_state *state_local);
 +
 +void dd_make_reverse_top(FILE *fplog,
 +                         gmx_domdec_t *dd, gmx_mtop_t *mtop,
 +                         gmx_vsite_t *vsite,
 +                         t_inputrec *ir, gmx_bool bBCheck);
 +
 +void dd_make_local_cgs(gmx_domdec_t *dd, t_block *lcgs);
 +
 +void dd_make_local_top(gmx_domdec_t *dd, gmx_domdec_zones_t *zones,
 +                       int npbcdim, matrix box,
 +                       rvec cellsize_min, ivec npulse,
 +                       t_forcerec *fr,
 +                       rvec *cgcm_or_x,
 +                       gmx_vsite_t *vsite,
 +                       gmx_mtop_t *top, gmx_localtop_t *ltop);
 +
 +void dd_sort_local_top(gmx_domdec_t *dd, t_mdatoms *mdatoms,
 +                       gmx_localtop_t *ltop);
 +/* Sort ltop->ilist when we are doing free energy. */
 +
 +gmx_localtop_t *dd_init_local_top(gmx_mtop_t *top_global);
 +
 +void dd_init_local_state(gmx_domdec_t *dd,
 +                         t_state *state_global, t_state *local_state);
 +
 +t_blocka *make_charge_group_links(gmx_mtop_t *mtop, gmx_domdec_t *dd,
 +                                  cginfo_mb_t *cginfo_mb);
 +
 +void dd_bonded_cg_distance(FILE *fplog, gmx_mtop_t *mtop,
 +                           t_inputrec *ir, rvec *x, matrix box,
 +                           gmx_bool bBCheck,
 +                           real *r_2b, real *r_mb);
 +
 +void write_dd_pdb(const char *fn, gmx_int64_t step, const char *title,
 +                  gmx_mtop_t *mtop,
 +                  t_commrec *cr,
 +                  int natoms, rvec x[], matrix box);
 +/* Dump a pdb file with the current DD home + communicated atoms.
 + * When natoms=-1, dump all known atoms.
 + */
 +
 +
 +/* In domdec_setup.c */
 +
 +real comm_box_frac(ivec dd_nc, real cutoff, gmx_ddbox_t *ddbox);
 +/* Returns the volume fraction of the system that is communicated */
 +
 +real dd_choose_grid(FILE *fplog,
 +                    t_commrec *cr, gmx_domdec_t *dd, t_inputrec *ir,
 +                    gmx_mtop_t *mtop, matrix box, gmx_ddbox_t *ddbox,
 +                    gmx_bool bDynLoadBal, real dlb_scale,
 +                    real cellsize_limit, real cutoff_dd,
 +                    gmx_bool bInterCGBondeds);
 +/* Determines the optimal DD cell setup dd->nc and possibly npmenodes
 + * for the system.
 + * On the master node returns the actual cellsize limit used.
 + */
 +
 +
 +/* In domdec_box.c */
 +
 +void set_ddbox(gmx_domdec_t *dd, gmx_bool bMasterState, t_commrec *cr_sum,
 +               t_inputrec *ir, matrix box,
 +               gmx_bool bCalcUnboundedSize, t_block *cgs, rvec *x,
 +               gmx_ddbox_t *ddbox);
 +
 +void set_ddbox_cr(t_commrec *cr, ivec *dd_nc,
 +                  t_inputrec *ir, matrix box, t_block *cgs, rvec *x,
 +                  gmx_ddbox_t *ddbox);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif  /* _domdec_h */
index ab70d9cc26d38efc14ed5017bf41d9cecb79c299,0000000000000000000000000000000000000000..0476d241518aed33759ebab9a56736f34407082b
mode 100644,000000..100644
--- /dev/null
@@@ -1,1704 -1,0 +1,1704 @@@
-                     dd_move_x_constraints(cr->dd, box, xp, NULL);
 +/*
 + * 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.
 + */
 +/* This file is completely threadsafe - keep it that way! */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include "main.h"
 +#include "constr.h"
 +#include "copyrite.h"
 +#include "physics.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "smalloc.h"
 +#include "mdrun.h"
 +#include "nrnb.h"
 +#include "domdec.h"
 +#include "mtop_util.h"
 +#include "gmx_omp_nthreads.h"
 +
 +#include "gromacs/fileio/gmxfio.h"
 +#include "gromacs/utility/gmxomp.h"
 +
 +typedef struct {
 +    int    b0;         /* first constraint for this thread */
 +    int    b1;         /* b1-1 is the last constraint for this thread */
 +    int    nind;       /* number of indices */
 +    int   *ind;        /* constraint index for updating atom data */
 +    int    nind_r;     /* number of indices */
 +    int   *ind_r;      /* constraint index for updating atom data */
 +    int    ind_nalloc; /* allocation size of ind and ind_r */
 +    tensor vir_r_m_dr; /* temporary variable for virial calculation */
 +} lincs_thread_t;
 +
 +typedef struct gmx_lincsdata {
 +    int             ncg;          /* the global number of constraints */
 +    int             ncg_flex;     /* the global number of flexible constraints */
 +    int             ncg_triangle; /* the global number of constraints in triangles */
 +    int             nIter;        /* the number of iterations */
 +    int             nOrder;       /* the order of the matrix expansion */
 +    int             nc;           /* the number of constraints */
 +    int             nc_alloc;     /* the number we allocated memory for */
 +    int             ncc;          /* the number of constraint connections */
 +    int             ncc_alloc;    /* the number we allocated memory for */
 +    real            matlam;       /* the FE lambda value used for filling blc and blmf */
 +    real           *bllen0;       /* the reference distance in topology A */
 +    real           *ddist;        /* the reference distance in top B - the r.d. in top A */
 +    int            *bla;          /* the atom pairs involved in the constraints */
 +    real           *blc;          /* 1/sqrt(invmass1 + invmass2) */
 +    real           *blc1;         /* as blc, but with all masses 1 */
 +    int            *blnr;         /* index into blbnb and blmf */
 +    int            *blbnb;        /* list of constraint connections */
 +    int             ntriangle;    /* the local number of constraints in triangles */
 +    int            *triangle;     /* the list of triangle constraints */
 +    int            *tri_bits;     /* the bits tell if the matrix element should be used */
 +    int             ncc_triangle; /* the number of constraint connections in triangles */
 +    gmx_bool        bCommIter;    /* communicate before each LINCS interation */
 +    real           *blmf;         /* matrix of mass factors for constraint connections */
 +    real           *blmf1;        /* as blmf, but with all masses 1 */
 +    real           *bllen;        /* the reference bond length */
 +    int             nth;          /* The number of threads doing LINCS */
 +    lincs_thread_t *th;           /* LINCS thread division */
 +    unsigned       *atf;          /* atom flags for thread parallelization */
 +    int             atf_nalloc;   /* allocation size of atf */
 +    /* arrays for temporary storage in the LINCS algorithm */
 +    rvec           *tmpv;
 +    real           *tmpncc;
 +    real           *tmp1;
 +    real           *tmp2;
 +    real           *tmp3;
 +    real           *tmp4;
 +    real           *mlambda; /* the Lagrange multipliers * -1 */
 +    /* storage for the constraint RMS relative deviation output */
 +    real            rmsd_data[3];
 +} t_gmx_lincsdata;
 +
 +real *lincs_rmsd_data(struct gmx_lincsdata *lincsd)
 +{
 +    return lincsd->rmsd_data;
 +}
 +
 +real lincs_rmsd(struct gmx_lincsdata *lincsd, gmx_bool bSD2)
 +{
 +    if (lincsd->rmsd_data[0] > 0)
 +    {
 +        return sqrt(lincsd->rmsd_data[bSD2 ? 2 : 1]/lincsd->rmsd_data[0]);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +/* Do a set of nrec LINCS matrix multiplications.
 + * This function will return with up to date thread-local
 + * constraint data, without an OpenMP barrier.
 + */
 +static void lincs_matrix_expand(const struct gmx_lincsdata *lincsd,
 +                                int b0, int b1,
 +                                const real *blcc,
 +                                real *rhs1, real *rhs2, real *sol)
 +{
 +    int        nrec, rec, b, j, n, nr0, nr1;
 +    real       mvb, *swap;
 +    int        ntriangle, tb, bits;
 +    const int *blnr     = lincsd->blnr, *blbnb = lincsd->blbnb;
 +    const int *triangle = lincsd->triangle, *tri_bits = lincsd->tri_bits;
 +
 +    ntriangle = lincsd->ntriangle;
 +    nrec      = lincsd->nOrder;
 +
 +    for (rec = 0; rec < nrec; rec++)
 +    {
 +#pragma omp barrier
 +        for (b = b0; b < b1; b++)
 +        {
 +            mvb = 0;
 +            for (n = blnr[b]; n < blnr[b+1]; n++)
 +            {
 +                j   = blbnb[n];
 +                mvb = mvb + blcc[n]*rhs1[j];
 +            }
 +            rhs2[b] = mvb;
 +            sol[b]  = sol[b] + mvb;
 +        }
 +        swap = rhs1;
 +        rhs1 = rhs2;
 +        rhs2 = swap;
 +    } /* nrec*(ncons+2*nrtot) flops */
 +
 +    if (ntriangle > 0)
 +    {
 +        /* Perform an extra nrec recursions for only the constraints
 +         * involved in rigid triangles.
 +         * In this way their accuracy should come close to those of the other
 +         * constraints, since traingles of constraints can produce eigenvalues
 +         * around 0.7, while the effective eigenvalue for bond constraints
 +         * is around 0.4 (and 0.7*0.7=0.5).
 +         */
 +        /* We need to copy the temporary array, since only the elements
 +         * for constraints involved in triangles are updated and then
 +         * the pointers are swapped. This saving copying the whole arrary.
 +         * We need barrier as other threads might still be reading from rhs2.
 +         */
 +#pragma omp barrier
 +        for (b = b0; b < b1; b++)
 +        {
 +            rhs2[b] = rhs1[b];
 +        }
 +#pragma omp barrier
 +#pragma omp master
 +        {
 +            for (rec = 0; rec < nrec; rec++)
 +            {
 +                for (tb = 0; tb < ntriangle; tb++)
 +                {
 +                    b    = triangle[tb];
 +                    bits = tri_bits[tb];
 +                    mvb  = 0;
 +                    nr0  = blnr[b];
 +                    nr1  = blnr[b+1];
 +                    for (n = nr0; n < nr1; n++)
 +                    {
 +                        if (bits & (1<<(n-nr0)))
 +                        {
 +                            j   = blbnb[n];
 +                            mvb = mvb + blcc[n]*rhs1[j];
 +                        }
 +                    }
 +                    rhs2[b] = mvb;
 +                    sol[b]  = sol[b] + mvb;
 +                }
 +                swap = rhs1;
 +                rhs1 = rhs2;
 +                rhs2 = swap;
 +            }
 +        } /* flops count is missing here */
 +
 +        /* We need a barrier here as the calling routine will continue
 +         * to operate on the thread-local constraints without barrier.
 +         */
 +#pragma omp barrier
 +    }
 +}
 +
 +static void lincs_update_atoms_noind(int ncons, const int *bla,
 +                                     real prefac,
 +                                     const real *fac, rvec *r,
 +                                     const real *invmass,
 +                                     rvec *x)
 +{
 +    int  b, i, j;
 +    real mvb, im1, im2, tmp0, tmp1, tmp2;
 +
 +    if (invmass != NULL)
 +    {
 +        for (b = 0; b < ncons; b++)
 +        {
 +            i        = bla[2*b];
 +            j        = bla[2*b+1];
 +            mvb      = prefac*fac[b];
 +            im1      = invmass[i];
 +            im2      = invmass[j];
 +            tmp0     = r[b][0]*mvb;
 +            tmp1     = r[b][1]*mvb;
 +            tmp2     = r[b][2]*mvb;
 +            x[i][0] -= tmp0*im1;
 +            x[i][1] -= tmp1*im1;
 +            x[i][2] -= tmp2*im1;
 +            x[j][0] += tmp0*im2;
 +            x[j][1] += tmp1*im2;
 +            x[j][2] += tmp2*im2;
 +        } /* 16 ncons flops */
 +    }
 +    else
 +    {
 +        for (b = 0; b < ncons; b++)
 +        {
 +            i        = bla[2*b];
 +            j        = bla[2*b+1];
 +            mvb      = prefac*fac[b];
 +            tmp0     = r[b][0]*mvb;
 +            tmp1     = r[b][1]*mvb;
 +            tmp2     = r[b][2]*mvb;
 +            x[i][0] -= tmp0;
 +            x[i][1] -= tmp1;
 +            x[i][2] -= tmp2;
 +            x[j][0] += tmp0;
 +            x[j][1] += tmp1;
 +            x[j][2] += tmp2;
 +        }
 +    }
 +}
 +
 +static void lincs_update_atoms_ind(int ncons, const int *ind, const int *bla,
 +                                   real prefac,
 +                                   const real *fac, rvec *r,
 +                                   const real *invmass,
 +                                   rvec *x)
 +{
 +    int  bi, b, i, j;
 +    real mvb, im1, im2, tmp0, tmp1, tmp2;
 +
 +    if (invmass != NULL)
 +    {
 +        for (bi = 0; bi < ncons; bi++)
 +        {
 +            b        = ind[bi];
 +            i        = bla[2*b];
 +            j        = bla[2*b+1];
 +            mvb      = prefac*fac[b];
 +            im1      = invmass[i];
 +            im2      = invmass[j];
 +            tmp0     = r[b][0]*mvb;
 +            tmp1     = r[b][1]*mvb;
 +            tmp2     = r[b][2]*mvb;
 +            x[i][0] -= tmp0*im1;
 +            x[i][1] -= tmp1*im1;
 +            x[i][2] -= tmp2*im1;
 +            x[j][0] += tmp0*im2;
 +            x[j][1] += tmp1*im2;
 +            x[j][2] += tmp2*im2;
 +        } /* 16 ncons flops */
 +    }
 +    else
 +    {
 +        for (bi = 0; bi < ncons; bi++)
 +        {
 +            b        = ind[bi];
 +            i        = bla[2*b];
 +            j        = bla[2*b+1];
 +            mvb      = prefac*fac[b];
 +            tmp0     = r[b][0]*mvb;
 +            tmp1     = r[b][1]*mvb;
 +            tmp2     = r[b][2]*mvb;
 +            x[i][0] -= tmp0;
 +            x[i][1] -= tmp1;
 +            x[i][2] -= tmp2;
 +            x[j][0] += tmp0;
 +            x[j][1] += tmp1;
 +            x[j][2] += tmp2;
 +        } /* 16 ncons flops */
 +    }
 +}
 +
 +static void lincs_update_atoms(struct gmx_lincsdata *li, int th,
 +                               real prefac,
 +                               const real *fac, rvec *r,
 +                               const real *invmass,
 +                               rvec *x)
 +{
 +    if (li->nth == 1)
 +    {
 +        /* Single thread, we simply update for all constraints */
 +        lincs_update_atoms_noind(li->nc, li->bla, prefac, fac, r, invmass, x);
 +    }
 +    else
 +    {
 +        /* Update the atom vector components for our thread local
 +         * constraints that only access our local atom range.
 +         * This can be done without a barrier.
 +         */
 +        lincs_update_atoms_ind(li->th[th].nind, li->th[th].ind,
 +                               li->bla, prefac, fac, r, invmass, x);
 +
 +        if (li->th[li->nth].nind > 0)
 +        {
 +            /* Update the constraints that operate on atoms
 +             * in multiple thread atom blocks on the master thread.
 +             */
 +#pragma omp barrier
 +#pragma omp master
 +            {
 +                lincs_update_atoms_ind(li->th[li->nth].nind,
 +                                       li->th[li->nth].ind,
 +                                       li->bla, prefac, fac, r, invmass, x);
 +            }
 +        }
 +    }
 +}
 +
 +/* LINCS projection, works on derivatives of the coordinates */
 +static void do_lincsp(rvec *x, rvec *f, rvec *fp, t_pbc *pbc,
 +                      struct gmx_lincsdata *lincsd, int th,
 +                      real *invmass,
 +                      int econq, real *dvdlambda,
 +                      gmx_bool bCalcVir, tensor rmdf)
 +{
 +    int      b0, b1, b, i, j, k, n;
 +    real     tmp0, tmp1, tmp2, im1, im2, mvb, rlen, len, wfac, lam;
 +    rvec     dx;
 +    int     *bla, *blnr, *blbnb;
 +    rvec    *r;
 +    real    *blc, *blmf, *blcc, *rhs1, *rhs2, *sol;
 +
 +    b0 = lincsd->th[th].b0;
 +    b1 = lincsd->th[th].b1;
 +
 +    bla    = lincsd->bla;
 +    r      = lincsd->tmpv;
 +    blnr   = lincsd->blnr;
 +    blbnb  = lincsd->blbnb;
 +    if (econq != econqForce)
 +    {
 +        /* Use mass-weighted parameters */
 +        blc  = lincsd->blc;
 +        blmf = lincsd->blmf;
 +    }
 +    else
 +    {
 +        /* Use non mass-weighted parameters */
 +        blc  = lincsd->blc1;
 +        blmf = lincsd->blmf1;
 +    }
 +    blcc   = lincsd->tmpncc;
 +    rhs1   = lincsd->tmp1;
 +    rhs2   = lincsd->tmp2;
 +    sol    = lincsd->tmp3;
 +
 +    /* Compute normalized i-j vectors */
 +    if (pbc)
 +    {
 +        for (b = b0; b < b1; b++)
 +        {
 +            pbc_dx_aiuc(pbc, x[bla[2*b]], x[bla[2*b+1]], dx);
 +            unitv(dx, r[b]);
 +        }
 +    }
 +    else
 +    {
 +        for (b = b0; b < b1; b++)
 +        {
 +            rvec_sub(x[bla[2*b]], x[bla[2*b+1]], dx);
 +            unitv(dx, r[b]);
 +        } /* 16 ncons flops */
 +    }
 +
 +#pragma omp barrier
 +    for (b = b0; b < b1; b++)
 +    {
 +        tmp0 = r[b][0];
 +        tmp1 = r[b][1];
 +        tmp2 = r[b][2];
 +        i    = bla[2*b];
 +        j    = bla[2*b+1];
 +        for (n = blnr[b]; n < blnr[b+1]; n++)
 +        {
 +            k       = blbnb[n];
 +            blcc[n] = blmf[n]*(tmp0*r[k][0] + tmp1*r[k][1] + tmp2*r[k][2]);
 +        } /* 6 nr flops */
 +        mvb = blc[b]*(tmp0*(f[i][0] - f[j][0]) +
 +                      tmp1*(f[i][1] - f[j][1]) +
 +                      tmp2*(f[i][2] - f[j][2]));
 +        rhs1[b] = mvb;
 +        sol[b]  = mvb;
 +        /* 7 flops */
 +    }
 +    /* Together: 23*ncons + 6*nrtot flops */
 +
 +    lincs_matrix_expand(lincsd, b0, b1, blcc, rhs1, rhs2, sol);
 +    /* nrec*(ncons+2*nrtot) flops */
 +
 +    if (econq == econqDeriv_FlexCon)
 +    {
 +        /* We only want to constraint the flexible constraints,
 +         * so we mask out the normal ones by setting sol to 0.
 +         */
 +        for (b = b0; b < b1; b++)
 +        {
 +            if (!(lincsd->bllen0[b] == 0 && lincsd->ddist[b] == 0))
 +            {
 +                sol[b] = 0;
 +            }
 +        }
 +    }
 +
 +    /* We multiply sol by blc, so we can use lincs_update_atoms for OpenMP */
 +    for (b = b0; b < b1; b++)
 +    {
 +        sol[b] *= blc[b];
 +    }
 +
 +    /* When constraining forces, we should not use mass weighting,
 +     * so we pass invmass=NULL, which results in the use of 1 for all atoms.
 +     */
 +    lincs_update_atoms(lincsd, th, 1.0, sol, r,
 +                       (econq != econqForce) ? invmass : NULL, fp);
 +
 +    if (dvdlambda != NULL)
 +    {
 +#pragma omp barrier
 +        for (b = b0; b < b1; b++)
 +        {
 +            *dvdlambda -= sol[b]*lincsd->ddist[b];
 +        }
 +        /* 10 ncons flops */
 +    }
 +
 +    if (bCalcVir)
 +    {
 +        /* Constraint virial,
 +         * determines sum r_bond x delta f,
 +         * where delta f is the constraint correction
 +         * of the quantity that is being constrained.
 +         */
 +        for (b = b0; b < b1; b++)
 +        {
 +            mvb = lincsd->bllen[b]*sol[b];
 +            for (i = 0; i < DIM; i++)
 +            {
 +                tmp1 = mvb*r[b][i];
 +                for (j = 0; j < DIM; j++)
 +                {
 +                    rmdf[i][j] += tmp1*r[b][j];
 +                }
 +            }
 +        } /* 23 ncons flops */
 +    }
 +}
 +
 +static void do_lincs(rvec *x, rvec *xp, matrix box, t_pbc *pbc,
 +                     struct gmx_lincsdata *lincsd, int th,
 +                     real *invmass,
 +                     t_commrec *cr,
 +                     gmx_bool bCalcLambda,
 +                     real wangle, int *warn,
 +                     real invdt, rvec *v,
 +                     gmx_bool bCalcVir, tensor vir_r_m_dr)
 +{
 +    int      b0, b1, b, i, j, k, n, iter;
 +    real     tmp0, tmp1, tmp2, im1, im2, mvb, rlen, len, len2, dlen2, wfac;
 +    rvec     dx;
 +    int     *bla, *blnr, *blbnb;
 +    rvec    *r;
 +    real    *blc, *blmf, *bllen, *blcc, *rhs1, *rhs2, *sol, *blc_sol, *mlambda;
 +    int     *nlocat;
 +
 +    b0 = lincsd->th[th].b0;
 +    b1 = lincsd->th[th].b1;
 +
 +    bla     = lincsd->bla;
 +    r       = lincsd->tmpv;
 +    blnr    = lincsd->blnr;
 +    blbnb   = lincsd->blbnb;
 +    blc     = lincsd->blc;
 +    blmf    = lincsd->blmf;
 +    bllen   = lincsd->bllen;
 +    blcc    = lincsd->tmpncc;
 +    rhs1    = lincsd->tmp1;
 +    rhs2    = lincsd->tmp2;
 +    sol     = lincsd->tmp3;
 +    blc_sol = lincsd->tmp4;
 +    mlambda = lincsd->mlambda;
 +
 +    if (DOMAINDECOMP(cr) && cr->dd->constraints)
 +    {
 +        nlocat = dd_constraints_nlocalatoms(cr->dd);
 +    }
 +    else
 +    {
 +        nlocat = NULL;
 +    }
 +
 +    if (pbc)
 +    {
 +        /* Compute normalized i-j vectors */
 +        for (b = b0; b < b1; b++)
 +        {
 +            pbc_dx_aiuc(pbc, x[bla[2*b]], x[bla[2*b+1]], dx);
 +            unitv(dx, r[b]);
 +        }
 +#pragma omp barrier
 +        for (b = b0; b < b1; b++)
 +        {
 +            for (n = blnr[b]; n < blnr[b+1]; n++)
 +            {
 +                blcc[n] = blmf[n]*iprod(r[b], r[blbnb[n]]);
 +            }
 +            pbc_dx_aiuc(pbc, xp[bla[2*b]], xp[bla[2*b+1]], dx);
 +            mvb     = blc[b]*(iprod(r[b], dx) - bllen[b]);
 +            rhs1[b] = mvb;
 +            sol[b]  = mvb;
 +        }
 +    }
 +    else
 +    {
 +        /* Compute normalized i-j vectors */
 +        for (b = b0; b < b1; b++)
 +        {
 +            i       = bla[2*b];
 +            j       = bla[2*b+1];
 +            tmp0    = x[i][0] - x[j][0];
 +            tmp1    = x[i][1] - x[j][1];
 +            tmp2    = x[i][2] - x[j][2];
 +            rlen    = gmx_invsqrt(tmp0*tmp0+tmp1*tmp1+tmp2*tmp2);
 +            r[b][0] = rlen*tmp0;
 +            r[b][1] = rlen*tmp1;
 +            r[b][2] = rlen*tmp2;
 +        } /* 16 ncons flops */
 +
 +#pragma omp barrier
 +        for (b = b0; b < b1; b++)
 +        {
 +            tmp0 = r[b][0];
 +            tmp1 = r[b][1];
 +            tmp2 = r[b][2];
 +            len  = bllen[b];
 +            i    = bla[2*b];
 +            j    = bla[2*b+1];
 +            for (n = blnr[b]; n < blnr[b+1]; n++)
 +            {
 +                k       = blbnb[n];
 +                blcc[n] = blmf[n]*(tmp0*r[k][0] + tmp1*r[k][1] + tmp2*r[k][2]);
 +            } /* 6 nr flops */
 +            mvb = blc[b]*(tmp0*(xp[i][0] - xp[j][0]) +
 +                          tmp1*(xp[i][1] - xp[j][1]) +
 +                          tmp2*(xp[i][2] - xp[j][2]) - len);
 +            rhs1[b] = mvb;
 +            sol[b]  = mvb;
 +            /* 10 flops */
 +        }
 +        /* Together: 26*ncons + 6*nrtot flops */
 +    }
 +
 +    lincs_matrix_expand(lincsd, b0, b1, blcc, rhs1, rhs2, sol);
 +    /* nrec*(ncons+2*nrtot) flops */
 +
 +    for (b = b0; b < b1; b++)
 +    {
 +        mlambda[b] = blc[b]*sol[b];
 +    }
 +
 +    /* Update the coordinates */
 +    lincs_update_atoms(lincsd, th, 1.0, mlambda, r, invmass, xp);
 +
 +    /*
 +     ********  Correction for centripetal effects  ********
 +     */
 +
 +    wfac = cos(DEG2RAD*wangle);
 +    wfac = wfac*wfac;
 +
 +    for (iter = 0; iter < lincsd->nIter; iter++)
 +    {
 +        if ((lincsd->bCommIter && DOMAINDECOMP(cr) && cr->dd->constraints))
 +        {
 +#pragma omp barrier
 +#pragma omp master
 +            {
 +                /* Communicate the corrected non-local coordinates */
 +                if (DOMAINDECOMP(cr))
 +                {
++                    dd_move_x_constraints(cr->dd, box, xp, NULL, FALSE);
 +                }
 +            }
 +        }
 +
 +#pragma omp barrier
 +        for (b = b0; b < b1; b++)
 +        {
 +            len = bllen[b];
 +            if (pbc)
 +            {
 +                pbc_dx_aiuc(pbc, xp[bla[2*b]], xp[bla[2*b+1]], dx);
 +            }
 +            else
 +            {
 +                rvec_sub(xp[bla[2*b]], xp[bla[2*b+1]], dx);
 +            }
 +            len2  = len*len;
 +            dlen2 = 2*len2 - norm2(dx);
 +            if (dlen2 < wfac*len2 && (nlocat == NULL || nlocat[b]))
 +            {
 +                *warn = b;
 +            }
 +            if (dlen2 > 0)
 +            {
 +                mvb = blc[b]*(len - dlen2*gmx_invsqrt(dlen2));
 +            }
 +            else
 +            {
 +                mvb = blc[b]*len;
 +            }
 +            rhs1[b] = mvb;
 +            sol[b]  = mvb;
 +        } /* 20*ncons flops */
 +
 +        lincs_matrix_expand(lincsd, b0, b1, blcc, rhs1, rhs2, sol);
 +        /* nrec*(ncons+2*nrtot) flops */
 +
 +        for (b = b0; b < b1; b++)
 +        {
 +            mvb         = blc[b]*sol[b];
 +            blc_sol[b]  = mvb;
 +            mlambda[b] += mvb;
 +        }
 +
 +        /* Update the coordinates */
 +        lincs_update_atoms(lincsd, th, 1.0, blc_sol, r, invmass, xp);
 +    }
 +    /* nit*ncons*(37+9*nrec) flops */
 +
 +    if (v != NULL)
 +    {
 +        /* Update the velocities */
 +        lincs_update_atoms(lincsd, th, invdt, mlambda, r, invmass, v);
 +        /* 16 ncons flops */
 +    }
 +
 +    if (nlocat != NULL && bCalcLambda)
 +    {
 +        /* In lincs_update_atoms thread might cross-read mlambda */
 +#pragma omp barrier
 +
 +        /* Only account for local atoms */
 +        for (b = b0; b < b1; b++)
 +        {
 +            mlambda[b] *= 0.5*nlocat[b];
 +        }
 +    }
 +
 +    if (bCalcVir)
 +    {
 +        /* Constraint virial */
 +        for (b = b0; b < b1; b++)
 +        {
 +            tmp0 = -bllen[b]*mlambda[b];
 +            for (i = 0; i < DIM; i++)
 +            {
 +                tmp1 = tmp0*r[b][i];
 +                for (j = 0; j < DIM; j++)
 +                {
 +                    vir_r_m_dr[i][j] -= tmp1*r[b][j];
 +                }
 +            }
 +        } /* 22 ncons flops */
 +    }
 +
 +    /* Total:
 +     * 26*ncons + 6*nrtot + nrec*(ncons+2*nrtot)
 +     * + nit * (20*ncons + nrec*(ncons+2*nrtot) + 17 ncons)
 +     *
 +     * (26+nrec)*ncons + (6+2*nrec)*nrtot
 +     * + nit * ((37+nrec)*ncons + 2*nrec*nrtot)
 +     * if nit=1
 +     * (63+nrec)*ncons + (6+4*nrec)*nrtot
 +     */
 +}
 +
 +void set_lincs_matrix(struct gmx_lincsdata *li, real *invmass, real lambda)
 +{
 +    int        i, a1, a2, n, k, sign, center;
 +    int        end, nk, kk;
 +    const real invsqrt2 = 0.7071067811865475244;
 +
 +    for (i = 0; (i < li->nc); i++)
 +    {
 +        a1          = li->bla[2*i];
 +        a2          = li->bla[2*i+1];
 +        li->blc[i]  = gmx_invsqrt(invmass[a1] + invmass[a2]);
 +        li->blc1[i] = invsqrt2;
 +    }
 +
 +    /* Construct the coupling coefficient matrix blmf */
 +    li->ntriangle    = 0;
 +    li->ncc_triangle = 0;
 +    for (i = 0; (i < li->nc); i++)
 +    {
 +        a1 = li->bla[2*i];
 +        a2 = li->bla[2*i+1];
 +        for (n = li->blnr[i]; (n < li->blnr[i+1]); n++)
 +        {
 +            k = li->blbnb[n];
 +            if (a1 == li->bla[2*k] || a2 == li->bla[2*k+1])
 +            {
 +                sign = -1;
 +            }
 +            else
 +            {
 +                sign = 1;
 +            }
 +            if (a1 == li->bla[2*k] || a1 == li->bla[2*k+1])
 +            {
 +                center = a1;
 +                end    = a2;
 +            }
 +            else
 +            {
 +                center = a2;
 +                end    = a1;
 +            }
 +            li->blmf[n]  = sign*invmass[center]*li->blc[i]*li->blc[k];
 +            li->blmf1[n] = sign*0.5;
 +            if (li->ncg_triangle > 0)
 +            {
 +                /* Look for constraint triangles */
 +                for (nk = li->blnr[k]; (nk < li->blnr[k+1]); nk++)
 +                {
 +                    kk = li->blbnb[nk];
 +                    if (kk != i && kk != k &&
 +                        (li->bla[2*kk] == end || li->bla[2*kk+1] == end))
 +                    {
 +                        if (li->ntriangle == 0 ||
 +                            li->triangle[li->ntriangle-1] < i)
 +                        {
 +                            /* Add this constraint to the triangle list */
 +                            li->triangle[li->ntriangle] = i;
 +                            li->tri_bits[li->ntriangle] = 0;
 +                            li->ntriangle++;
 +                            if (li->blnr[i+1] - li->blnr[i] > sizeof(li->tri_bits[0])*8 - 1)
 +                            {
 +                                gmx_fatal(FARGS, "A constraint is connected to %d constraints, this is more than the %d allowed for constraints participating in triangles",
 +                                          li->blnr[i+1] - li->blnr[i],
 +                                          sizeof(li->tri_bits[0])*8-1);
 +                            }
 +                        }
 +                        li->tri_bits[li->ntriangle-1] |= (1<<(n-li->blnr[i]));
 +                        li->ncc_triangle++;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Of the %d constraints %d participate in triangles\n",
 +                li->nc, li->ntriangle);
 +        fprintf(debug, "There are %d couplings of which %d in triangles\n",
 +                li->ncc, li->ncc_triangle);
 +    }
 +
 +    /* Set matlam,
 +     * so we know with which lambda value the masses have been set.
 +     */
 +    li->matlam = lambda;
 +}
 +
 +static int count_triangle_constraints(t_ilist *ilist, t_blocka *at2con)
 +{
 +    int      ncon1, ncon_tot;
 +    int      c0, a00, a01, n1, c1, a10, a11, ac1, n2, c2, a20, a21;
 +    int      ncon_triangle;
 +    gmx_bool bTriangle;
 +    t_iatom *ia1, *ia2, *iap;
 +
 +    ncon1    = ilist[F_CONSTR].nr/3;
 +    ncon_tot = ncon1 + ilist[F_CONSTRNC].nr/3;
 +
 +    ia1 = ilist[F_CONSTR].iatoms;
 +    ia2 = ilist[F_CONSTRNC].iatoms;
 +
 +    ncon_triangle = 0;
 +    for (c0 = 0; c0 < ncon_tot; c0++)
 +    {
 +        bTriangle = FALSE;
 +        iap       = constr_iatomptr(ncon1, ia1, ia2, c0);
 +        a00       = iap[1];
 +        a01       = iap[2];
 +        for (n1 = at2con->index[a01]; n1 < at2con->index[a01+1]; n1++)
 +        {
 +            c1 = at2con->a[n1];
 +            if (c1 != c0)
 +            {
 +                iap = constr_iatomptr(ncon1, ia1, ia2, c1);
 +                a10 = iap[1];
 +                a11 = iap[2];
 +                if (a10 == a01)
 +                {
 +                    ac1 = a11;
 +                }
 +                else
 +                {
 +                    ac1 = a10;
 +                }
 +                for (n2 = at2con->index[ac1]; n2 < at2con->index[ac1+1]; n2++)
 +                {
 +                    c2 = at2con->a[n2];
 +                    if (c2 != c0 && c2 != c1)
 +                    {
 +                        iap = constr_iatomptr(ncon1, ia1, ia2, c2);
 +                        a20 = iap[1];
 +                        a21 = iap[2];
 +                        if (a20 == a00 || a21 == a00)
 +                        {
 +                            bTriangle = TRUE;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        if (bTriangle)
 +        {
 +            ncon_triangle++;
 +        }
 +    }
 +
 +    return ncon_triangle;
 +}
 +
 +static gmx_bool more_than_two_sequential_constraints(const t_ilist  *ilist,
 +                                                     const t_blocka *at2con)
 +{
 +    t_iatom  *ia1, *ia2, *iap;
 +    int       ncon1, ncon_tot, c;
 +    int       a1, a2;
 +    gmx_bool  bMoreThanTwoSequentialConstraints;
 +
 +    ncon1    = ilist[F_CONSTR].nr/3;
 +    ncon_tot = ncon1 + ilist[F_CONSTRNC].nr/3;
 +
 +    ia1 = ilist[F_CONSTR].iatoms;
 +    ia2 = ilist[F_CONSTRNC].iatoms;
 +
 +    bMoreThanTwoSequentialConstraints = FALSE;
 +    for (c = 0; c < ncon_tot && !bMoreThanTwoSequentialConstraints; c++)
 +    {
 +        iap = constr_iatomptr(ncon1, ia1, ia2, c);
 +        a1  = iap[1];
 +        a2  = iap[2];
 +        /* Check if this constraint has constraints connected at both atoms */
 +        if (at2con->index[a1+1] - at2con->index[a1] > 1 &&
 +            at2con->index[a2+1] - at2con->index[a2] > 1)
 +        {
 +            bMoreThanTwoSequentialConstraints = TRUE;
 +        }
 +    }
 +
 +    return bMoreThanTwoSequentialConstraints;
 +}
 +
 +static int int_comp(const void *a, const void *b)
 +{
 +    return (*(int *)a) - (*(int *)b);
 +}
 +
 +gmx_lincsdata_t init_lincs(FILE *fplog, gmx_mtop_t *mtop,
 +                           int nflexcon_global, t_blocka *at2con,
 +                           gmx_bool bPLINCS, int nIter, int nProjOrder)
 +{
 +    struct gmx_lincsdata *li;
 +    int                   mb;
 +    gmx_moltype_t        *molt;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "\nInitializing%s LINear Constraint Solver\n",
 +                bPLINCS ? " Parallel" : "");
 +    }
 +
 +    snew(li, 1);
 +
 +    li->ncg      =
 +        gmx_mtop_ftype_count(mtop, F_CONSTR) +
 +        gmx_mtop_ftype_count(mtop, F_CONSTRNC);
 +    li->ncg_flex = nflexcon_global;
 +
 +    li->nIter  = nIter;
 +    li->nOrder = nProjOrder;
 +
 +    li->ncg_triangle = 0;
 +    li->bCommIter    = FALSE;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molt              = &mtop->moltype[mtop->molblock[mb].type];
 +        li->ncg_triangle +=
 +            mtop->molblock[mb].nmol*
 +            count_triangle_constraints(molt->ilist,
 +                                       &at2con[mtop->molblock[mb].type]);
 +        if (bPLINCS && li->bCommIter == FALSE)
 +        {
 +            /* Check if we need to communicate not only before LINCS,
 +             * but also before each iteration.
 +             * The check for only two sequential constraints is only
 +             * useful for the common case of H-bond only constraints.
 +             * With more effort we could also make it useful for small
 +             * molecules with nr. sequential constraints <= nOrder-1.
 +             */
 +            li->bCommIter = (li->nOrder < 1 || more_than_two_sequential_constraints(molt->ilist, &at2con[mtop->molblock[mb].type]));
 +        }
 +    }
 +    if (debug && bPLINCS)
 +    {
 +        fprintf(debug, "PLINCS communication before each iteration: %d\n",
 +                li->bCommIter);
 +    }
 +
 +    /* LINCS can run on any number of threads.
 +     * Currently the number is fixed for the whole simulation,
 +     * but it could be set in set_lincs().
 +     */
 +    li->nth = gmx_omp_nthreads_get(emntLINCS);
 +    if (li->nth == 1)
 +    {
 +        snew(li->th, 1);
 +    }
 +    else
 +    {
 +        /* Allocate an extra elements for "thread-overlap" constraints */
 +        snew(li->th, li->nth+1);
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "LINCS: using %d threads\n", li->nth);
 +    }
 +
 +    if (bPLINCS || li->ncg_triangle > 0)
 +    {
 +        please_cite(fplog, "Hess2008a");
 +    }
 +    else
 +    {
 +        please_cite(fplog, "Hess97a");
 +    }
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "The number of constraints is %d\n", li->ncg);
 +        if (bPLINCS)
 +        {
 +            fprintf(fplog, "There are inter charge-group constraints,\n"
 +                    "will communicate selected coordinates each lincs iteration\n");
 +        }
 +        if (li->ncg_triangle > 0)
 +        {
 +            fprintf(fplog,
 +                    "%d constraints are involved in constraint triangles,\n"
 +                    "will apply an additional matrix expansion of order %d for couplings\n"
 +                    "between constraints inside triangles\n",
 +                    li->ncg_triangle, li->nOrder);
 +        }
 +    }
 +
 +    return li;
 +}
 +
 +/* Sets up the work division over the threads */
 +static void lincs_thread_setup(struct gmx_lincsdata *li, int natoms)
 +{
 +    lincs_thread_t *li_m;
 +    int             th;
 +    unsigned       *atf;
 +    int             a;
 +
 +    if (natoms > li->atf_nalloc)
 +    {
 +        li->atf_nalloc = over_alloc_large(natoms);
 +        srenew(li->atf, li->atf_nalloc);
 +    }
 +
 +    atf = li->atf;
 +    /* Clear the atom flags */
 +    for (a = 0; a < natoms; a++)
 +    {
 +        atf[a] = 0;
 +    }
 +
 +    for (th = 0; th < li->nth; th++)
 +    {
 +        lincs_thread_t *li_th;
 +        int             b;
 +
 +        li_th = &li->th[th];
 +
 +        /* The constraints are divided equally over the threads */
 +        li_th->b0 = (li->nc* th   )/li->nth;
 +        li_th->b1 = (li->nc*(th+1))/li->nth;
 +
 +        if (th < sizeof(*atf)*8)
 +        {
 +            /* For each atom set a flag for constraints from each */
 +            for (b = li_th->b0; b < li_th->b1; b++)
 +            {
 +                atf[li->bla[b*2]  ] |= (1U<<th);
 +                atf[li->bla[b*2+1]] |= (1U<<th);
 +            }
 +        }
 +    }
 +
 +#pragma omp parallel for num_threads(li->nth) schedule(static)
 +    for (th = 0; th < li->nth; th++)
 +    {
 +        lincs_thread_t *li_th;
 +        unsigned        mask;
 +        int             b;
 +
 +        li_th = &li->th[th];
 +
 +        if (li_th->b1 - li_th->b0 > li_th->ind_nalloc)
 +        {
 +            li_th->ind_nalloc = over_alloc_large(li_th->b1-li_th->b0);
 +            srenew(li_th->ind, li_th->ind_nalloc);
 +            srenew(li_th->ind_r, li_th->ind_nalloc);
 +        }
 +
 +        if (th < sizeof(*atf)*8)
 +        {
 +            mask = (1U<<th) - 1U;
 +
 +            li_th->nind   = 0;
 +            li_th->nind_r = 0;
 +            for (b = li_th->b0; b < li_th->b1; b++)
 +            {
 +                /* We let the constraint with the lowest thread index
 +                 * operate on atoms with constraints from multiple threads.
 +                 */
 +                if (((atf[li->bla[b*2]]   & mask) == 0) &&
 +                    ((atf[li->bla[b*2+1]] & mask) == 0))
 +                {
 +                    /* Add the constraint to the local atom update index */
 +                    li_th->ind[li_th->nind++] = b;
 +                }
 +                else
 +                {
 +                    /* Add the constraint to the rest block */
 +                    li_th->ind_r[li_th->nind_r++] = b;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            /* We are out of bits, assign all constraints to rest */
 +            for (b = li_th->b0; b < li_th->b1; b++)
 +            {
 +                li_th->ind_r[li_th->nind_r++] = b;
 +            }
 +        }
 +    }
 +
 +    /* We need to copy all constraints which have not be assigned
 +     * to a thread to a separate list which will be handled by one thread.
 +     */
 +    li_m = &li->th[li->nth];
 +
 +    li_m->nind = 0;
 +    for (th = 0; th < li->nth; th++)
 +    {
 +        lincs_thread_t *li_th;
 +        int             b;
 +
 +        li_th   = &li->th[th];
 +
 +        if (li_m->nind + li_th->nind_r > li_m->ind_nalloc)
 +        {
 +            li_m->ind_nalloc = over_alloc_large(li_m->nind+li_th->nind_r);
 +            srenew(li_m->ind, li_m->ind_nalloc);
 +        }
 +
 +        for (b = 0; b < li_th->nind_r; b++)
 +        {
 +            li_m->ind[li_m->nind++] = li_th->ind_r[b];
 +        }
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "LINCS thread %d: %d constraints\n",
 +                    th, li_th->nind);
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "LINCS thread r: %d constraints\n",
 +                li_m->nind);
 +    }
 +}
 +
 +
 +void set_lincs(t_idef *idef, t_mdatoms *md,
 +               gmx_bool bDynamics, t_commrec *cr,
 +               struct gmx_lincsdata *li)
 +{
 +    int          start, natoms, nflexcon;
 +    t_blocka     at2con;
 +    t_iatom     *iatom;
 +    int          i, k, ncc_alloc, ni, con, nconnect, concon;
 +    int          type, a1, a2;
 +    real         lenA = 0, lenB;
 +    gmx_bool     bLocal;
 +
 +    li->nc  = 0;
 +    li->ncc = 0;
 +    /* Zero the thread index ranges.
 +     * Otherwise without local constraints we could return with old ranges.
 +     */
 +    for (i = 0; i < li->nth; i++)
 +    {
 +        li->th[i].b0   = 0;
 +        li->th[i].b1   = 0;
 +        li->th[i].nind = 0;
 +    }
 +    if (li->nth > 1)
 +    {
 +        li->th[li->nth].nind = 0;
 +    }
 +
 +    /* This is the local topology, so there are only F_CONSTR constraints */
 +    if (idef->il[F_CONSTR].nr == 0)
 +    {
 +        /* There are no constraints,
 +         * we do not need to fill any data structures.
 +         */
 +        return;
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Building the LINCS connectivity\n");
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        if (cr->dd->constraints)
 +        {
 +            dd_get_constraint_range(cr->dd, &start, &natoms);
 +        }
 +        else
 +        {
 +            natoms = cr->dd->nat_home;
 +        }
 +        start = 0;
 +    }
 +    else
 +    {
 +        start  = 0;
 +        natoms = md->homenr;
 +    }
 +    at2con = make_at2con(start, natoms, idef->il, idef->iparams, bDynamics,
 +                         &nflexcon);
 +
 +
 +    if (idef->il[F_CONSTR].nr/3 > li->nc_alloc || li->nc_alloc == 0)
 +    {
 +        li->nc_alloc = over_alloc_dd(idef->il[F_CONSTR].nr/3);
 +        srenew(li->bllen0, li->nc_alloc);
 +        srenew(li->ddist, li->nc_alloc);
 +        srenew(li->bla, 2*li->nc_alloc);
 +        srenew(li->blc, li->nc_alloc);
 +        srenew(li->blc1, li->nc_alloc);
 +        srenew(li->blnr, li->nc_alloc+1);
 +        srenew(li->bllen, li->nc_alloc);
 +        srenew(li->tmpv, li->nc_alloc);
 +        srenew(li->tmp1, li->nc_alloc);
 +        srenew(li->tmp2, li->nc_alloc);
 +        srenew(li->tmp3, li->nc_alloc);
 +        srenew(li->tmp4, li->nc_alloc);
 +        srenew(li->mlambda, li->nc_alloc);
 +        if (li->ncg_triangle > 0)
 +        {
 +            /* This is allocating too much, but it is difficult to improve */
 +            srenew(li->triangle, li->nc_alloc);
 +            srenew(li->tri_bits, li->nc_alloc);
 +        }
 +    }
 +
 +    iatom = idef->il[F_CONSTR].iatoms;
 +
 +    ncc_alloc   = li->ncc_alloc;
 +    li->blnr[0] = 0;
 +
 +    ni = idef->il[F_CONSTR].nr/3;
 +
 +    con           = 0;
 +    nconnect      = 0;
 +    li->blnr[con] = nconnect;
 +    for (i = 0; i < ni; i++)
 +    {
 +        bLocal = TRUE;
 +        type   = iatom[3*i];
 +        a1     = iatom[3*i+1];
 +        a2     = iatom[3*i+2];
 +        lenA   = idef->iparams[type].constr.dA;
 +        lenB   = idef->iparams[type].constr.dB;
 +        /* Skip the flexible constraints when not doing dynamics */
 +        if (bDynamics || lenA != 0 || lenB != 0)
 +        {
 +            li->bllen0[con]  = lenA;
 +            li->ddist[con]   = lenB - lenA;
 +            /* Set the length to the topology A length */
 +            li->bllen[con]   = li->bllen0[con];
 +            li->bla[2*con]   = a1;
 +            li->bla[2*con+1] = a2;
 +            /* Construct the constraint connection matrix blbnb */
 +            for (k = at2con.index[a1-start]; k < at2con.index[a1-start+1]; k++)
 +            {
 +                concon = at2con.a[k];
 +                if (concon != i)
 +                {
 +                    if (nconnect >= ncc_alloc)
 +                    {
 +                        ncc_alloc = over_alloc_small(nconnect+1);
 +                        srenew(li->blbnb, ncc_alloc);
 +                    }
 +                    li->blbnb[nconnect++] = concon;
 +                }
 +            }
 +            for (k = at2con.index[a2-start]; k < at2con.index[a2-start+1]; k++)
 +            {
 +                concon = at2con.a[k];
 +                if (concon != i)
 +                {
 +                    if (nconnect+1 > ncc_alloc)
 +                    {
 +                        ncc_alloc = over_alloc_small(nconnect+1);
 +                        srenew(li->blbnb, ncc_alloc);
 +                    }
 +                    li->blbnb[nconnect++] = concon;
 +                }
 +            }
 +            li->blnr[con+1] = nconnect;
 +
 +            if (cr->dd == NULL)
 +            {
 +                /* Order the blbnb matrix to optimize memory access */
 +                qsort(&(li->blbnb[li->blnr[con]]), li->blnr[con+1]-li->blnr[con],
 +                      sizeof(li->blbnb[0]), int_comp);
 +            }
 +            /* Increase the constraint count */
 +            con++;
 +        }
 +    }
 +
 +    done_blocka(&at2con);
 +
 +    /* This is the real number of constraints,
 +     * without dynamics the flexible constraints are not present.
 +     */
 +    li->nc = con;
 +
 +    li->ncc = li->blnr[con];
 +    if (cr->dd == NULL)
 +    {
 +        /* Since the matrix is static, we can free some memory */
 +        ncc_alloc = li->ncc;
 +        srenew(li->blbnb, ncc_alloc);
 +    }
 +
 +    if (ncc_alloc > li->ncc_alloc)
 +    {
 +        li->ncc_alloc = ncc_alloc;
 +        srenew(li->blmf, li->ncc_alloc);
 +        srenew(li->blmf1, li->ncc_alloc);
 +        srenew(li->tmpncc, li->ncc_alloc);
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Number of constraints is %d, couplings %d\n",
 +                li->nc, li->ncc);
 +    }
 +
 +    if (li->nth == 1)
 +    {
 +        li->th[0].b0 = 0;
 +        li->th[0].b1 = li->nc;
 +    }
 +    else
 +    {
 +        lincs_thread_setup(li, md->nr);
 +    }
 +
 +    set_lincs_matrix(li, md->invmass, md->lambda);
 +}
 +
 +static void lincs_warning(FILE *fplog,
 +                          gmx_domdec_t *dd, rvec *x, rvec *xprime, t_pbc *pbc,
 +                          int ncons, int *bla, real *bllen, real wangle,
 +                          int maxwarn, int *warncount)
 +{
 +    int  b, i, j;
 +    rvec v0, v1;
 +    real wfac, d0, d1, cosine;
 +    char buf[STRLEN];
 +
 +    wfac = cos(DEG2RAD*wangle);
 +
 +    sprintf(buf, "bonds that rotated more than %g degrees:\n"
 +            " atom 1 atom 2  angle  previous, current, constraint length\n",
 +            wangle);
 +    fprintf(stderr, "%s", buf);
 +    if (fplog)
 +    {
 +        fprintf(fplog, "%s", buf);
 +    }
 +
 +    for (b = 0; b < ncons; b++)
 +    {
 +        i = bla[2*b];
 +        j = bla[2*b+1];
 +        if (pbc)
 +        {
 +            pbc_dx_aiuc(pbc, x[i], x[j], v0);
 +            pbc_dx_aiuc(pbc, xprime[i], xprime[j], v1);
 +        }
 +        else
 +        {
 +            rvec_sub(x[i], x[j], v0);
 +            rvec_sub(xprime[i], xprime[j], v1);
 +        }
 +        d0     = norm(v0);
 +        d1     = norm(v1);
 +        cosine = iprod(v0, v1)/(d0*d1);
 +        if (cosine < wfac)
 +        {
 +            sprintf(buf, " %6d %6d  %5.1f  %8.4f %8.4f    %8.4f\n",
 +                    ddglatnr(dd, i), ddglatnr(dd, j),
 +                    RAD2DEG*acos(cosine), d0, d1, bllen[b]);
 +            fprintf(stderr, "%s", buf);
 +            if (fplog)
 +            {
 +                fprintf(fplog, "%s", buf);
 +            }
 +            if (!gmx_isfinite(d1))
 +            {
 +                gmx_fatal(FARGS, "Bond length not finite.");
 +            }
 +
 +            (*warncount)++;
 +        }
 +    }
 +    if (*warncount > maxwarn)
 +    {
 +        too_many_constraint_warnings(econtLINCS, *warncount);
 +    }
 +}
 +
 +static void cconerr(gmx_domdec_t *dd,
 +                    int ncons, int *bla, real *bllen, rvec *x, t_pbc *pbc,
 +                    real *ncons_loc, real *ssd, real *max, int *imax)
 +{
 +    real       len, d, ma, ssd2, r2;
 +    int       *nlocat, count, b, im;
 +    rvec       dx;
 +
 +    if (dd && dd->constraints)
 +    {
 +        nlocat = dd_constraints_nlocalatoms(dd);
 +    }
 +    else
 +    {
 +        nlocat = 0;
 +    }
 +
 +    ma    = 0;
 +    ssd2  = 0;
 +    im    = 0;
 +    count = 0;
 +    for (b = 0; b < ncons; b++)
 +    {
 +        if (pbc)
 +        {
 +            pbc_dx_aiuc(pbc, x[bla[2*b]], x[bla[2*b+1]], dx);
 +        }
 +        else
 +        {
 +            rvec_sub(x[bla[2*b]], x[bla[2*b+1]], dx);
 +        }
 +        r2  = norm2(dx);
 +        len = r2*gmx_invsqrt(r2);
 +        d   = fabs(len/bllen[b]-1);
 +        if (d > ma && (nlocat == NULL || nlocat[b]))
 +        {
 +            ma = d;
 +            im = b;
 +        }
 +        if (nlocat == NULL)
 +        {
 +            ssd2 += d*d;
 +            count++;
 +        }
 +        else
 +        {
 +            ssd2  += nlocat[b]*d*d;
 +            count += nlocat[b];
 +        }
 +    }
 +
 +    *ncons_loc = (nlocat ? 0.5 : 1)*count;
 +    *ssd       = (nlocat ? 0.5 : 1)*ssd2;
 +    *max       = ma;
 +    *imax      = im;
 +}
 +
 +gmx_bool constrain_lincs(FILE *fplog, gmx_bool bLog, gmx_bool bEner,
 +                         t_inputrec *ir,
 +                         gmx_int64_t step,
 +                         struct gmx_lincsdata *lincsd, t_mdatoms *md,
 +                         t_commrec *cr,
 +                         rvec *x, rvec *xprime, rvec *min_proj,
 +                         matrix box, t_pbc *pbc,
 +                         real lambda, real *dvdlambda,
 +                         real invdt, rvec *v,
 +                         gmx_bool bCalcVir, tensor vir_r_m_dr,
 +                         int econq,
 +                         t_nrnb *nrnb,
 +                         int maxwarn, int *warncount)
 +{
 +    char      buf[STRLEN], buf2[22], buf3[STRLEN];
 +    int       i, warn, p_imax, error;
 +    real      ncons_loc, p_ssd, p_max = 0;
 +    rvec      dx;
 +    gmx_bool  bOK;
 +
 +    bOK = TRUE;
 +
 +    if (lincsd->nc == 0 && cr->dd == NULL)
 +    {
 +        if (bLog || bEner)
 +        {
 +            lincsd->rmsd_data[0] = 0;
 +            if (ir->eI == eiSD2 && v == NULL)
 +            {
 +                i = 2;
 +            }
 +            else
 +            {
 +                i = 1;
 +            }
 +            lincsd->rmsd_data[i] = 0;
 +        }
 +
 +        return bOK;
 +    }
 +
 +    if (econq == econqCoord)
 +    {
 +        if (ir->efep != efepNO)
 +        {
 +            if (md->nMassPerturbed && lincsd->matlam != md->lambda)
 +            {
 +                set_lincs_matrix(lincsd, md->invmass, md->lambda);
 +            }
 +
 +            for (i = 0; i < lincsd->nc; i++)
 +            {
 +                lincsd->bllen[i] = lincsd->bllen0[i] + lambda*lincsd->ddist[i];
 +            }
 +        }
 +
 +        if (lincsd->ncg_flex)
 +        {
 +            /* Set the flexible constraint lengths to the old lengths */
 +            if (pbc != NULL)
 +            {
 +                for (i = 0; i < lincsd->nc; i++)
 +                {
 +                    if (lincsd->bllen[i] == 0)
 +                    {
 +                        pbc_dx_aiuc(pbc, x[lincsd->bla[2*i]], x[lincsd->bla[2*i+1]], dx);
 +                        lincsd->bllen[i] = norm(dx);
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < lincsd->nc; i++)
 +                {
 +                    if (lincsd->bllen[i] == 0)
 +                    {
 +                        lincsd->bllen[i] =
 +                            sqrt(distance2(x[lincsd->bla[2*i]],
 +                                           x[lincsd->bla[2*i+1]]));
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (bLog && fplog)
 +        {
 +            cconerr(cr->dd, lincsd->nc, lincsd->bla, lincsd->bllen, xprime, pbc,
 +                    &ncons_loc, &p_ssd, &p_max, &p_imax);
 +        }
 +
 +        /* This warn var can be updated by multiple threads
 +         * at the same time. But as we only need to detect
 +         * if a warning occured or not, this is not an issue.
 +         */
 +        warn = -1;
 +
 +        /* The OpenMP parallel region of constrain_lincs for coords */
 +#pragma omp parallel num_threads(lincsd->nth)
 +        {
 +            int th = gmx_omp_get_thread_num();
 +
 +            clear_mat(lincsd->th[th].vir_r_m_dr);
 +
 +            do_lincs(x, xprime, box, pbc, lincsd, th,
 +                     md->invmass, cr,
 +                     bCalcVir || (ir->efep != efepNO),
 +                     ir->LincsWarnAngle, &warn,
 +                     invdt, v, bCalcVir,
 +                     th == 0 ? vir_r_m_dr : lincsd->th[th].vir_r_m_dr);
 +        }
 +
 +        if (ir->efep != efepNO)
 +        {
 +            real dt_2, dvdl = 0;
 +
 +            dt_2 = 1.0/(ir->delta_t*ir->delta_t);
 +            for (i = 0; (i < lincsd->nc); i++)
 +            {
 +                dvdl -= lincsd->mlambda[i]*dt_2*lincsd->ddist[i];
 +            }
 +            *dvdlambda += dvdl;
 +        }
 +
 +        if (bLog && fplog && lincsd->nc > 0)
 +        {
 +            fprintf(fplog, "   Rel. Constraint Deviation:  RMS         MAX     between atoms\n");
 +            fprintf(fplog, "       Before LINCS          %.6f    %.6f %6d %6d\n",
 +                    sqrt(p_ssd/ncons_loc), p_max,
 +                    ddglatnr(cr->dd, lincsd->bla[2*p_imax]),
 +                    ddglatnr(cr->dd, lincsd->bla[2*p_imax+1]));
 +        }
 +        if (bLog || bEner)
 +        {
 +            cconerr(cr->dd, lincsd->nc, lincsd->bla, lincsd->bllen, xprime, pbc,
 +                    &ncons_loc, &p_ssd, &p_max, &p_imax);
 +            /* Check if we are doing the second part of SD */
 +            if (ir->eI == eiSD2 && v == NULL)
 +            {
 +                i = 2;
 +            }
 +            else
 +            {
 +                i = 1;
 +            }
 +            lincsd->rmsd_data[0] = ncons_loc;
 +            lincsd->rmsd_data[i] = p_ssd;
 +        }
 +        else
 +        {
 +            lincsd->rmsd_data[0] = 0;
 +            lincsd->rmsd_data[1] = 0;
 +            lincsd->rmsd_data[2] = 0;
 +        }
 +        if (bLog && fplog && lincsd->nc > 0)
 +        {
 +            fprintf(fplog,
 +                    "        After LINCS          %.6f    %.6f %6d %6d\n\n",
 +                    sqrt(p_ssd/ncons_loc), p_max,
 +                    ddglatnr(cr->dd, lincsd->bla[2*p_imax]),
 +                    ddglatnr(cr->dd, lincsd->bla[2*p_imax+1]));
 +        }
 +
 +        if (warn >= 0)
 +        {
 +            if (maxwarn >= 0)
 +            {
 +                cconerr(cr->dd, lincsd->nc, lincsd->bla, lincsd->bllen, xprime, pbc,
 +                        &ncons_loc, &p_ssd, &p_max, &p_imax);
 +                if (MULTISIM(cr))
 +                {
 +                    sprintf(buf3, " in simulation %d", cr->ms->sim);
 +                }
 +                else
 +                {
 +                    buf3[0] = 0;
 +                }
 +                sprintf(buf, "\nStep %s, time %g (ps)  LINCS WARNING%s\n"
 +                        "relative constraint deviation after LINCS:\n"
 +                        "rms %.6f, max %.6f (between atoms %d and %d)\n",
 +                        gmx_step_str(step, buf2), ir->init_t+step*ir->delta_t,
 +                        buf3,
 +                        sqrt(p_ssd/ncons_loc), p_max,
 +                        ddglatnr(cr->dd, lincsd->bla[2*p_imax]),
 +                        ddglatnr(cr->dd, lincsd->bla[2*p_imax+1]));
 +                if (fplog)
 +                {
 +                    fprintf(fplog, "%s", buf);
 +                }
 +                fprintf(stderr, "%s", buf);
 +                lincs_warning(fplog, cr->dd, x, xprime, pbc,
 +                              lincsd->nc, lincsd->bla, lincsd->bllen,
 +                              ir->LincsWarnAngle, maxwarn, warncount);
 +            }
 +            bOK = (p_max < 0.5);
 +        }
 +
 +        if (lincsd->ncg_flex)
 +        {
 +            for (i = 0; (i < lincsd->nc); i++)
 +            {
 +                if (lincsd->bllen0[i] == 0 && lincsd->ddist[i] == 0)
 +                {
 +                    lincsd->bllen[i] = 0;
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* The OpenMP parallel region of constrain_lincs for derivatives */
 +#pragma omp parallel num_threads(lincsd->nth)
 +        {
 +            int th = gmx_omp_get_thread_num();
 +
 +            do_lincsp(x, xprime, min_proj, pbc, lincsd, th,
 +                      md->invmass, econq, ir->efep != efepNO ? dvdlambda : NULL,
 +                      bCalcVir, th == 0 ? vir_r_m_dr : lincsd->th[th].vir_r_m_dr);
 +        }
 +    }
 +
 +    if (bCalcVir && lincsd->nth > 1)
 +    {
 +        for (i = 1; i < lincsd->nth; i++)
 +        {
 +            m_add(vir_r_m_dr, lincsd->th[i].vir_r_m_dr, vir_r_m_dr);
 +        }
 +    }
 +
 +    /* count assuming nit=1 */
 +    inc_nrnb(nrnb, eNR_LINCS, lincsd->nc);
 +    inc_nrnb(nrnb, eNR_LINCSMAT, (2+lincsd->nOrder)*lincsd->ncc);
 +    if (lincsd->ntriangle > 0)
 +    {
 +        inc_nrnb(nrnb, eNR_LINCSMAT, lincsd->nOrder*lincsd->ncc_triangle);
 +    }
 +    if (v)
 +    {
 +        inc_nrnb(nrnb, eNR_CONSTR_V, lincsd->nc*2);
 +    }
 +    if (bCalcVir)
 +    {
 +        inc_nrnb(nrnb, eNR_CONSTR_VIR, lincsd->nc);
 +    }
 +
 +    return bOK;
 +}
index 4f01f54654aab436b7c3e44aca2d6b0709ea9752,0000000000000000000000000000000000000000..a237a084579c8b3c12f249d89c95abdd4ac2a9f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,1476 -1,0 +1,1476 @@@
-         dd_move_x_constraints(cr->dd, box, x, xprime);
 +/*
 + * 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.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "gromacs/fileio/confio.h"
 +#include "types/commrec.h"
 +#include "constr.h"
 +#include "copyrite.h"
 +#include "invblock.h"
 +#include "main.h"
 +#include "mdrun.h"
 +#include "nrnb.h"
 +#include "smalloc.h"
 +#include "vec.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "txtdump.h"
 +#include "domdec.h"
 +#include "gromacs/fileio/pdbio.h"
 +#include "splitter.h"
 +#include "mtop_util.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "macros.h"
 +#include "gmx_omp_nthreads.h"
 +#include "gromacs/essentialdynamics/edsam.h"
 +#include "gromacs/pulling/pull.h"
 +
 +
 +typedef struct gmx_constr {
 +    int                ncon_tot;       /* The total number of constraints    */
 +    int                nflexcon;       /* The number of flexible constraints */
 +    int                n_at2con_mt;    /* The size of at2con = #moltypes     */
 +    t_blocka          *at2con_mt;      /* A list of atoms to constraints     */
 +    int                n_at2settle_mt; /* The size of at2settle = #moltypes  */
 +    int              **at2settle_mt;   /* A list of atoms to settles         */
 +    gmx_bool           bInterCGsettles;
 +    gmx_lincsdata_t    lincsd;         /* LINCS data                         */
 +    gmx_shakedata_t    shaked;         /* SHAKE data                         */
 +    gmx_settledata_t   settled;        /* SETTLE data                        */
 +    int                nblocks;        /* The number of SHAKE blocks         */
 +    int               *sblock;         /* The SHAKE blocks                   */
 +    int                sblock_nalloc;  /* The allocation size of sblock      */
 +    real              *lagr;           /* Lagrange multipliers for SHAKE     */
 +    int                lagr_nalloc;    /* The allocation size of lagr        */
 +    int                maxwarn;        /* The maximum number of warnings     */
 +    int                warncount_lincs;
 +    int                warncount_settle;
 +    gmx_edsam_t        ed;            /* The essential dynamics data        */
 +
 +    tensor            *vir_r_m_dr_th; /* Thread local working data          */
 +    int               *settle_error;  /* Thread local working data          */
 +
 +    gmx_mtop_t        *warn_mtop;     /* Only used for printing warnings    */
 +} t_gmx_constr;
 +
 +typedef struct {
 +    atom_id iatom[3];
 +    atom_id blocknr;
 +} t_sortblock;
 +
 +static void *init_vetavars(t_vetavars *vars,
 +                           gmx_bool constr_deriv,
 +                           real veta, real vetanew, t_inputrec *ir, gmx_ekindata_t *ekind, gmx_bool bPscal)
 +{
 +    double g;
 +    int    i;
 +
 +    /* first, set the alpha integrator variable */
 +    if ((ir->opts.nrdf[0] > 0) && bPscal)
 +    {
 +        vars->alpha = 1.0 + DIM/((double)ir->opts.nrdf[0]);
 +    }
 +    else
 +    {
 +        vars->alpha = 1.0;
 +    }
 +    g             = 0.5*veta*ir->delta_t;
 +    vars->rscale  = exp(g)*series_sinhx(g);
 +    g             = -0.25*vars->alpha*veta*ir->delta_t;
 +    vars->vscale  = exp(g)*series_sinhx(g);
 +    vars->rvscale = vars->vscale*vars->rscale;
 +    vars->veta    = vetanew;
 +
 +    if (constr_deriv)
 +    {
 +        snew(vars->vscale_nhc, ir->opts.ngtc);
 +        if ((ekind == NULL) || (!bPscal))
 +        {
 +            for (i = 0; i < ir->opts.ngtc; i++)
 +            {
 +                vars->vscale_nhc[i] = 1;
 +            }
 +        }
 +        else
 +        {
 +            for (i = 0; i < ir->opts.ngtc; i++)
 +            {
 +                vars->vscale_nhc[i] = ekind->tcstat[i].vscale_nhc;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        vars->vscale_nhc = NULL;
 +    }
 +
 +    return vars;
 +}
 +
 +static void free_vetavars(t_vetavars *vars)
 +{
 +    if (vars->vscale_nhc != NULL)
 +    {
 +        sfree(vars->vscale_nhc);
 +    }
 +}
 +
 +static int pcomp(const void *p1, const void *p2)
 +{
 +    int          db;
 +    atom_id      min1, min2, max1, max2;
 +    t_sortblock *a1 = (t_sortblock *)p1;
 +    t_sortblock *a2 = (t_sortblock *)p2;
 +
 +    db = a1->blocknr-a2->blocknr;
 +
 +    if (db != 0)
 +    {
 +        return db;
 +    }
 +
 +    min1 = min(a1->iatom[1], a1->iatom[2]);
 +    max1 = max(a1->iatom[1], a1->iatom[2]);
 +    min2 = min(a2->iatom[1], a2->iatom[2]);
 +    max2 = max(a2->iatom[1], a2->iatom[2]);
 +
 +    if (min1 == min2)
 +    {
 +        return max1-max2;
 +    }
 +    else
 +    {
 +        return min1-min2;
 +    }
 +}
 +
 +int n_flexible_constraints(struct gmx_constr *constr)
 +{
 +    int nflexcon;
 +
 +    if (constr)
 +    {
 +        nflexcon = constr->nflexcon;
 +    }
 +    else
 +    {
 +        nflexcon = 0;
 +    }
 +
 +    return nflexcon;
 +}
 +
 +void too_many_constraint_warnings(int eConstrAlg, int warncount)
 +{
 +    const char *abort = "- aborting to avoid logfile runaway.\n"
 +        "This normally happens when your system is not sufficiently equilibrated,"
 +        "or if you are changing lambda too fast in free energy simulations.\n";
 +
 +    gmx_fatal(FARGS,
 +              "Too many %s warnings (%d)\n"
 +              "If you know what you are doing you can %s"
 +              "set the environment variable GMX_MAXCONSTRWARN to -1,\n"
 +              "but normally it is better to fix the problem",
 +              (eConstrAlg == econtLINCS) ? "LINCS" : "SETTLE", warncount,
 +              (eConstrAlg == econtLINCS) ?
 +              "adjust the lincs warning threshold in your mdp file\nor " : "\n");
 +}
 +
 +static void write_constr_pdb(const char *fn, const char *title,
 +                             gmx_mtop_t *mtop,
 +                             int start, int homenr, t_commrec *cr,
 +                             rvec x[], matrix box)
 +{
 +    char          fname[STRLEN], format[STRLEN];
 +    FILE         *out;
 +    int           dd_ac0 = 0, dd_ac1 = 0, i, ii, resnr;
 +    gmx_domdec_t *dd;
 +    char         *anm, *resnm;
 +
 +    dd = NULL;
 +    if (DOMAINDECOMP(cr))
 +    {
 +        dd = cr->dd;
 +        dd_get_constraint_range(dd, &dd_ac0, &dd_ac1);
 +        start  = 0;
 +        homenr = dd_ac1;
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        sprintf(fname, "%s_n%d.pdb", fn, cr->sim_nodeid);
 +    }
 +    else
 +    {
 +        sprintf(fname, "%s.pdb", fn);
 +    }
 +    sprintf(format, "%s\n", get_pdbformat());
 +
 +    out = gmx_fio_fopen(fname, "w");
 +
 +    fprintf(out, "TITLE     %s\n", title);
 +    gmx_write_pdb_box(out, -1, box);
 +    for (i = start; i < start+homenr; i++)
 +    {
 +        if (dd != NULL)
 +        {
 +            if (i >= dd->nat_home && i < dd_ac0)
 +            {
 +                continue;
 +            }
 +            ii = dd->gatindex[i];
 +        }
 +        else
 +        {
 +            ii = i;
 +        }
 +        gmx_mtop_atominfo_global(mtop, ii, &anm, &resnr, &resnm);
 +        fprintf(out, format, "ATOM", (ii+1)%100000,
 +                anm, resnm, ' ', resnr%10000, ' ',
 +                10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ]);
 +    }
 +    fprintf(out, "TER\n");
 +
 +    gmx_fio_fclose(out);
 +}
 +
 +static void dump_confs(FILE *fplog, gmx_int64_t step, gmx_mtop_t *mtop,
 +                       int start, int homenr, t_commrec *cr,
 +                       rvec x[], rvec xprime[], matrix box)
 +{
 +    char  buf[256], buf2[22];
 +
 +    char *env = getenv("GMX_SUPPRESS_DUMP");
 +    if (env)
 +    {
 +        return;
 +    }
 +
 +    sprintf(buf, "step%sb", gmx_step_str(step, buf2));
 +    write_constr_pdb(buf, "initial coordinates",
 +                     mtop, start, homenr, cr, x, box);
 +    sprintf(buf, "step%sc", gmx_step_str(step, buf2));
 +    write_constr_pdb(buf, "coordinates after constraining",
 +                     mtop, start, homenr, cr, xprime, box);
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Wrote pdb files with previous and current coordinates\n");
 +    }
 +    fprintf(stderr, "Wrote pdb files with previous and current coordinates\n");
 +}
 +
 +static void pr_sortblock(FILE *fp, const char *title, int nsb, t_sortblock sb[])
 +{
 +    int i;
 +
 +    fprintf(fp, "%s\n", title);
 +    for (i = 0; (i < nsb); i++)
 +    {
 +        fprintf(fp, "i: %5d, iatom: (%5d %5d %5d), blocknr: %5d\n",
 +                i, sb[i].iatom[0], sb[i].iatom[1], sb[i].iatom[2],
 +                sb[i].blocknr);
 +    }
 +}
 +
 +gmx_bool constrain(FILE *fplog, gmx_bool bLog, gmx_bool bEner,
 +                   struct gmx_constr *constr,
 +                   t_idef *idef, t_inputrec *ir, gmx_ekindata_t *ekind,
 +                   t_commrec *cr,
 +                   gmx_int64_t step, int delta_step,
 +                   t_mdatoms *md,
 +                   rvec *x, rvec *xprime, rvec *min_proj,
 +                   gmx_bool bMolPBC, matrix box,
 +                   real lambda, real *dvdlambda,
 +                   rvec *v, tensor *vir,
 +                   t_nrnb *nrnb, int econq, gmx_bool bPscal,
 +                   real veta, real vetanew)
 +{
 +    gmx_bool    bOK, bDump;
 +    int         start, homenr, nrend;
 +    int         i, j, d;
 +    int         ncons, settle_error;
 +    tensor      vir_r_m_dr;
 +    rvec       *vstor;
 +    real        invdt, vir_fac, t;
 +    t_ilist    *settle;
 +    int         nsettle;
 +    t_pbc       pbc, *pbc_null;
 +    char        buf[22];
 +    t_vetavars  vetavar;
 +    int         nth, th;
 +
 +    if (econq == econqForceDispl && !EI_ENERGY_MINIMIZATION(ir->eI))
 +    {
 +        gmx_incons("constrain called for forces displacements while not doing energy minimization, can not do this while the LINCS and SETTLE constraint connection matrices are mass weighted");
 +    }
 +
 +    bOK   = TRUE;
 +    bDump = FALSE;
 +
 +    start  = 0;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    /* set constants for pressure control integration */
 +    init_vetavars(&vetavar, econq != econqCoord,
 +                  veta, vetanew, ir, ekind, bPscal);
 +
 +    if (ir->delta_t == 0)
 +    {
 +        invdt = 0;
 +    }
 +    else
 +    {
 +        invdt  = 1/ir->delta_t;
 +    }
 +
 +    if (ir->efep != efepNO && EI_DYNAMICS(ir->eI))
 +    {
 +        /* Set the constraint lengths for the step at which this configuration
 +         * is meant to be. The invmasses should not be changed.
 +         */
 +        lambda += delta_step*ir->fepvals->delta_lambda;
 +    }
 +
 +    if (vir != NULL)
 +    {
 +        clear_mat(vir_r_m_dr);
 +    }
 +
 +    where();
 +
 +    settle  = &idef->il[F_SETTLE];
 +    nsettle = settle->nr/(1+NRAL(F_SETTLE));
 +
 +    if (nsettle > 0)
 +    {
 +        nth = gmx_omp_nthreads_get(emntSETTLE);
 +    }
 +    else
 +    {
 +        nth = 1;
 +    }
 +
 +    if (nth > 1 && constr->vir_r_m_dr_th == NULL)
 +    {
 +        snew(constr->vir_r_m_dr_th, nth);
 +        snew(constr->settle_error, nth);
 +    }
 +
 +    settle_error = -1;
 +
 +    /* We do not need full pbc when constraints do not cross charge groups,
 +     * i.e. when dd->constraint_comm==NULL.
 +     * Note that PBC for constraints is different from PBC for bondeds.
 +     * For constraints there is both forward and backward communication.
 +     */
 +    if (ir->ePBC != epbcNONE &&
 +        (cr->dd || bMolPBC) && !(cr->dd && cr->dd->constraint_comm == NULL))
 +    {
 +        /* With pbc=screw the screw has been changed to a shift
 +         * by the constraint coordinate communication routine,
 +         * so that here we can use normal pbc.
 +         */
 +        pbc_null = set_pbc_dd(&pbc, ir->ePBC, cr->dd, FALSE, box);
 +    }
 +    else
 +    {
 +        pbc_null = NULL;
 +    }
 +
 +    /* Communicate the coordinates required for the non-local constraints
 +     * for LINCS and/or SETTLE.
 +     */
 +    if (cr->dd)
 +    {
++        dd_move_x_constraints(cr->dd, box, x, xprime, econq == econqCoord);
 +    }
 +
 +    if (constr->lincsd != NULL)
 +    {
 +        bOK = constrain_lincs(fplog, bLog, bEner, ir, step, constr->lincsd, md, cr,
 +                              x, xprime, min_proj,
 +                              box, pbc_null, lambda, dvdlambda,
 +                              invdt, v, vir != NULL, vir_r_m_dr,
 +                              econq, nrnb,
 +                              constr->maxwarn, &constr->warncount_lincs);
 +        if (!bOK && constr->maxwarn >= 0)
 +        {
 +            if (fplog != NULL)
 +            {
 +                fprintf(fplog, "Constraint error in algorithm %s at step %s\n",
 +                        econstr_names[econtLINCS], gmx_step_str(step, buf));
 +            }
 +            bDump = TRUE;
 +        }
 +    }
 +
 +    if (constr->nblocks > 0)
 +    {
 +        switch (econq)
 +        {
 +            case (econqCoord):
 +                bOK = bshakef(fplog, constr->shaked,
 +                              md->invmass, constr->nblocks, constr->sblock,
 +                              idef, ir, x, xprime, nrnb,
 +                              constr->lagr, lambda, dvdlambda,
 +                              invdt, v, vir != NULL, vir_r_m_dr,
 +                              constr->maxwarn >= 0, econq, &vetavar);
 +                break;
 +            case (econqVeloc):
 +                bOK = bshakef(fplog, constr->shaked,
 +                              md->invmass, constr->nblocks, constr->sblock,
 +                              idef, ir, x, min_proj, nrnb,
 +                              constr->lagr, lambda, dvdlambda,
 +                              invdt, NULL, vir != NULL, vir_r_m_dr,
 +                              constr->maxwarn >= 0, econq, &vetavar);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Internal error, SHAKE called for constraining something else than coordinates");
 +                break;
 +        }
 +
 +        if (!bOK && constr->maxwarn >= 0)
 +        {
 +            if (fplog != NULL)
 +            {
 +                fprintf(fplog, "Constraint error in algorithm %s at step %s\n",
 +                        econstr_names[econtSHAKE], gmx_step_str(step, buf));
 +            }
 +            bDump = TRUE;
 +        }
 +    }
 +
 +    if (nsettle > 0)
 +    {
 +        int calcvir_atom_end;
 +
 +        if (vir == NULL)
 +        {
 +            calcvir_atom_end = 0;
 +        }
 +        else
 +        {
 +            calcvir_atom_end = md->homenr;
 +        }
 +
 +        switch (econq)
 +        {
 +            case econqCoord:
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +                for (th = 0; th < nth; th++)
 +                {
 +                    int start_th, end_th;
 +
 +                    if (th > 0)
 +                    {
 +                        clear_mat(constr->vir_r_m_dr_th[th]);
 +                    }
 +
 +                    start_th = (nsettle* th   )/nth;
 +                    end_th   = (nsettle*(th+1))/nth;
 +                    if (start_th >= 0 && end_th - start_th > 0)
 +                    {
 +                        csettle(constr->settled,
 +                                end_th-start_th,
 +                                settle->iatoms+start_th*(1+NRAL(F_SETTLE)),
 +                                pbc_null,
 +                                x[0], xprime[0],
 +                                invdt, v ? v[0] : NULL, calcvir_atom_end,
 +                                th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th],
 +                                th == 0 ? &settle_error : &constr->settle_error[th],
 +                                &vetavar);
 +                    }
 +                }
 +                inc_nrnb(nrnb, eNR_SETTLE, nsettle);
 +                if (v != NULL)
 +                {
 +                    inc_nrnb(nrnb, eNR_CONSTR_V, nsettle*3);
 +                }
 +                if (vir != NULL)
 +                {
 +                    inc_nrnb(nrnb, eNR_CONSTR_VIR, nsettle*3);
 +                }
 +                break;
 +            case econqVeloc:
 +            case econqDeriv:
 +            case econqForce:
 +            case econqForceDispl:
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +                for (th = 0; th < nth; th++)
 +                {
 +                    int start_th, end_th;
 +
 +                    if (th > 0)
 +                    {
 +                        clear_mat(constr->vir_r_m_dr_th[th]);
 +                    }
 +
 +                    start_th = (nsettle* th   )/nth;
 +                    end_th   = (nsettle*(th+1))/nth;
 +
 +                    if (start_th >= 0 && end_th - start_th > 0)
 +                    {
 +                        settle_proj(constr->settled, econq,
 +                                    end_th-start_th,
 +                                    settle->iatoms+start_th*(1+NRAL(F_SETTLE)),
 +                                    pbc_null,
 +                                    x,
 +                                    xprime, min_proj, calcvir_atom_end,
 +                                    th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th],
 +                                    &vetavar);
 +                    }
 +                }
 +                /* This is an overestimate */
 +                inc_nrnb(nrnb, eNR_SETTLE, nsettle);
 +                break;
 +            case econqDeriv_FlexCon:
 +                /* Nothing to do, since the are no flexible constraints in settles */
 +                break;
 +            default:
 +                gmx_incons("Unknown constraint quantity for settle");
 +        }
 +    }
 +
 +    if (settle->nr > 0)
 +    {
 +        /* Combine virial and error info of the other threads */
 +        for (i = 1; i < nth; i++)
 +        {
 +            m_add(vir_r_m_dr, constr->vir_r_m_dr_th[i], vir_r_m_dr);
 +            settle_error = constr->settle_error[i];
 +        }
 +
 +        if (econq == econqCoord && settle_error >= 0)
 +        {
 +            bOK = FALSE;
 +            if (constr->maxwarn >= 0)
 +            {
 +                char buf[256];
 +                sprintf(buf,
 +                        "\nstep " "%"GMX_PRId64 ": Water molecule starting at atom %d can not be "
 +                        "settled.\nCheck for bad contacts and/or reduce the timestep if appropriate.\n",
 +                        step, ddglatnr(cr->dd, settle->iatoms[settle_error*(1+NRAL(F_SETTLE))+1]));
 +                if (fplog)
 +                {
 +                    fprintf(fplog, "%s", buf);
 +                }
 +                fprintf(stderr, "%s", buf);
 +                constr->warncount_settle++;
 +                if (constr->warncount_settle > constr->maxwarn)
 +                {
 +                    too_many_constraint_warnings(-1, constr->warncount_settle);
 +                }
 +                bDump = TRUE;
 +            }
 +        }
 +    }
 +
 +    free_vetavars(&vetavar);
 +
 +    if (vir != NULL)
 +    {
 +        switch (econq)
 +        {
 +            case econqCoord:
 +                vir_fac = 0.5/(ir->delta_t*ir->delta_t);
 +                break;
 +            case econqVeloc:
 +                vir_fac = 0.5/ir->delta_t;
 +                break;
 +            case econqForce:
 +            case econqForceDispl:
 +                vir_fac = 0.5;
 +                break;
 +            default:
 +                vir_fac = 0;
 +                gmx_incons("Unsupported constraint quantity for virial");
 +        }
 +
 +        if (EI_VV(ir->eI))
 +        {
 +            vir_fac *= 2;  /* only constraining over half the distance here */
 +        }
 +        for (i = 0; i < DIM; i++)
 +        {
 +            for (j = 0; j < DIM; j++)
 +            {
 +                (*vir)[i][j] = vir_fac*vir_r_m_dr[i][j];
 +            }
 +        }
 +    }
 +
 +    if (bDump)
 +    {
 +        dump_confs(fplog, step, constr->warn_mtop, start, homenr, cr, x, xprime, box);
 +    }
 +
 +    if (econq == econqCoord)
 +    {
 +        if (ir->ePull == epullCONSTRAINT)
 +        {
 +            if (EI_DYNAMICS(ir->eI))
 +            {
 +                t = ir->init_t + (step + delta_step)*ir->delta_t;
 +            }
 +            else
 +            {
 +                t = ir->init_t;
 +            }
 +            set_pbc(&pbc, ir->ePBC, box);
 +            pull_constraint(ir->pull, md, &pbc, cr, ir->delta_t, t, x, xprime, v, *vir);
 +        }
 +        if (constr->ed && delta_step > 0)
 +        {
 +            /* apply the essential dynamcs constraints here */
 +            do_edsam(ir, step, cr, xprime, v, box, constr->ed);
 +        }
 +    }
 +
 +    return bOK;
 +}
 +
 +real *constr_rmsd_data(struct gmx_constr *constr)
 +{
 +    if (constr->lincsd)
 +    {
 +        return lincs_rmsd_data(constr->lincsd);
 +    }
 +    else
 +    {
 +        return NULL;
 +    }
 +}
 +
 +real constr_rmsd(struct gmx_constr *constr, gmx_bool bSD2)
 +{
 +    if (constr->lincsd)
 +    {
 +        return lincs_rmsd(constr->lincsd, bSD2);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static void make_shake_sblock_serial(struct gmx_constr *constr,
 +                                     t_idef *idef, t_mdatoms *md)
 +{
 +    int          i, j, m, ncons;
 +    int          bstart, bnr;
 +    t_blocka     sblocks;
 +    t_sortblock *sb;
 +    t_iatom     *iatom;
 +    atom_id     *inv_sblock;
 +
 +    /* Since we are processing the local topology,
 +     * the F_CONSTRNC ilist has been concatenated to the F_CONSTR ilist.
 +     */
 +    ncons = idef->il[F_CONSTR].nr/3;
 +
 +    init_blocka(&sblocks);
 +    gen_sblocks(NULL, 0, md->homenr, idef, &sblocks, FALSE);
 +
 +    /*
 +       bstart=(idef->nodeid > 0) ? blocks->multinr[idef->nodeid-1] : 0;
 +       nblocks=blocks->multinr[idef->nodeid] - bstart;
 +     */
 +    bstart          = 0;
 +    constr->nblocks = sblocks.nr;
 +    if (debug)
 +    {
 +        fprintf(debug, "ncons: %d, bstart: %d, nblocks: %d\n",
 +                ncons, bstart, constr->nblocks);
 +    }
 +
 +    /* Calculate block number for each atom */
 +    inv_sblock = make_invblocka(&sblocks, md->nr);
 +
 +    done_blocka(&sblocks);
 +
 +    /* Store the block number in temp array and
 +     * sort the constraints in order of the sblock number
 +     * and the atom numbers, really sorting a segment of the array!
 +     */
 +#ifdef DEBUGIDEF
 +    pr_idef(fplog, 0, "Before Sort", idef);
 +#endif
 +    iatom = idef->il[F_CONSTR].iatoms;
 +    snew(sb, ncons);
 +    for (i = 0; (i < ncons); i++, iatom += 3)
 +    {
 +        for (m = 0; (m < 3); m++)
 +        {
 +            sb[i].iatom[m] = iatom[m];
 +        }
 +        sb[i].blocknr = inv_sblock[iatom[1]];
 +    }
 +
 +    /* Now sort the blocks */
 +    if (debug)
 +    {
 +        pr_sortblock(debug, "Before sorting", ncons, sb);
 +        fprintf(debug, "Going to sort constraints\n");
 +    }
 +
 +    qsort(sb, ncons, (size_t)sizeof(*sb), pcomp);
 +
 +    if (debug)
 +    {
 +        pr_sortblock(debug, "After sorting", ncons, sb);
 +    }
 +
 +    iatom = idef->il[F_CONSTR].iatoms;
 +    for (i = 0; (i < ncons); i++, iatom += 3)
 +    {
 +        for (m = 0; (m < 3); m++)
 +        {
 +            iatom[m] = sb[i].iatom[m];
 +        }
 +    }
 +#ifdef DEBUGIDEF
 +    pr_idef(fplog, 0, "After Sort", idef);
 +#endif
 +
 +    j = 0;
 +    snew(constr->sblock, constr->nblocks+1);
 +    bnr = -2;
 +    for (i = 0; (i < ncons); i++)
 +    {
 +        if (sb[i].blocknr != bnr)
 +        {
 +            bnr                 = sb[i].blocknr;
 +            constr->sblock[j++] = 3*i;
 +        }
 +    }
 +    /* Last block... */
 +    constr->sblock[j++] = 3*ncons;
 +
 +    if (j != (constr->nblocks+1))
 +    {
 +        fprintf(stderr, "bstart: %d\n", bstart);
 +        fprintf(stderr, "j: %d, nblocks: %d, ncons: %d\n",
 +                j, constr->nblocks, ncons);
 +        for (i = 0; (i < ncons); i++)
 +        {
 +            fprintf(stderr, "i: %5d  sb[i].blocknr: %5u\n", i, sb[i].blocknr);
 +        }
 +        for (j = 0; (j <= constr->nblocks); j++)
 +        {
 +            fprintf(stderr, "sblock[%3d]=%5d\n", j, (int)constr->sblock[j]);
 +        }
 +        gmx_fatal(FARGS, "DEATH HORROR: "
 +                  "sblocks does not match idef->il[F_CONSTR]");
 +    }
 +    sfree(sb);
 +    sfree(inv_sblock);
 +}
 +
 +static void make_shake_sblock_dd(struct gmx_constr *constr,
 +                                 t_ilist *ilcon, t_block *cgs,
 +                                 gmx_domdec_t *dd)
 +{
 +    int      ncons, c, cg;
 +    t_iatom *iatom;
 +
 +    if (dd->ncg_home+1 > constr->sblock_nalloc)
 +    {
 +        constr->sblock_nalloc = over_alloc_dd(dd->ncg_home+1);
 +        srenew(constr->sblock, constr->sblock_nalloc);
 +    }
 +
 +    ncons           = ilcon->nr/3;
 +    iatom           = ilcon->iatoms;
 +    constr->nblocks = 0;
 +    cg              = 0;
 +    for (c = 0; c < ncons; c++)
 +    {
 +        if (c == 0 || iatom[1] >= cgs->index[cg+1])
 +        {
 +            constr->sblock[constr->nblocks++] = 3*c;
 +            while (iatom[1] >= cgs->index[cg+1])
 +            {
 +                cg++;
 +            }
 +        }
 +        iatom += 3;
 +    }
 +    constr->sblock[constr->nblocks] = 3*ncons;
 +}
 +
 +t_blocka make_at2con(int start, int natoms,
 +                     t_ilist *ilist, t_iparams *iparams,
 +                     gmx_bool bDynamics, int *nflexiblecons)
 +{
 +    int      *count, ncon, con, con_tot, nflexcon, ftype, i, a;
 +    t_iatom  *ia;
 +    t_blocka  at2con;
 +    gmx_bool  bFlexCon;
 +
 +    snew(count, natoms);
 +    nflexcon = 0;
 +    for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
 +    {
 +        ncon = ilist[ftype].nr/3;
 +        ia   = ilist[ftype].iatoms;
 +        for (con = 0; con < ncon; con++)
 +        {
 +            bFlexCon = (iparams[ia[0]].constr.dA == 0 &&
 +                        iparams[ia[0]].constr.dB == 0);
 +            if (bFlexCon)
 +            {
 +                nflexcon++;
 +            }
 +            if (bDynamics || !bFlexCon)
 +            {
 +                for (i = 1; i < 3; i++)
 +                {
 +                    a = ia[i] - start;
 +                    count[a]++;
 +                }
 +            }
 +            ia += 3;
 +        }
 +    }
 +    *nflexiblecons = nflexcon;
 +
 +    at2con.nr           = natoms;
 +    at2con.nalloc_index = at2con.nr+1;
 +    snew(at2con.index, at2con.nalloc_index);
 +    at2con.index[0] = 0;
 +    for (a = 0; a < natoms; a++)
 +    {
 +        at2con.index[a+1] = at2con.index[a] + count[a];
 +        count[a]          = 0;
 +    }
 +    at2con.nra      = at2con.index[natoms];
 +    at2con.nalloc_a = at2con.nra;
 +    snew(at2con.a, at2con.nalloc_a);
 +
 +    /* The F_CONSTRNC constraints have constraint numbers
 +     * that continue after the last F_CONSTR constraint.
 +     */
 +    con_tot = 0;
 +    for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
 +    {
 +        ncon = ilist[ftype].nr/3;
 +        ia   = ilist[ftype].iatoms;
 +        for (con = 0; con < ncon; con++)
 +        {
 +            bFlexCon = (iparams[ia[0]].constr.dA == 0 &&
 +                        iparams[ia[0]].constr.dB == 0);
 +            if (bDynamics || !bFlexCon)
 +            {
 +                for (i = 1; i < 3; i++)
 +                {
 +                    a = ia[i] - start;
 +                    at2con.a[at2con.index[a]+count[a]++] = con_tot;
 +                }
 +            }
 +            con_tot++;
 +            ia += 3;
 +        }
 +    }
 +
 +    sfree(count);
 +
 +    return at2con;
 +}
 +
 +static int *make_at2settle(int natoms, const t_ilist *ilist)
 +{
 +    int *at2s;
 +    int  a, stride, s;
 +
 +    snew(at2s, natoms);
 +    /* Set all to no settle */
 +    for (a = 0; a < natoms; a++)
 +    {
 +        at2s[a] = -1;
 +    }
 +
 +    stride = 1 + NRAL(F_SETTLE);
 +
 +    for (s = 0; s < ilist->nr; s += stride)
 +    {
 +        at2s[ilist->iatoms[s+1]] = s/stride;
 +        at2s[ilist->iatoms[s+2]] = s/stride;
 +        at2s[ilist->iatoms[s+3]] = s/stride;
 +    }
 +
 +    return at2s;
 +}
 +
 +void set_constraints(struct gmx_constr *constr,
 +                     gmx_localtop_t *top, t_inputrec *ir,
 +                     t_mdatoms *md, t_commrec *cr)
 +{
 +    t_idef  *idef;
 +    int      ncons;
 +    t_ilist *settle;
 +    int      iO, iH;
 +
 +    idef = &top->idef;
 +
 +    if (constr->ncon_tot > 0)
 +    {
 +        /* We are using the local topology,
 +         * so there are only F_CONSTR constraints.
 +         */
 +        ncons = idef->il[F_CONSTR].nr/3;
 +
 +        /* With DD we might also need to call LINCS with ncons=0 for
 +         * communicating coordinates to other nodes that do have constraints.
 +         */
 +        if (ir->eConstrAlg == econtLINCS)
 +        {
 +            set_lincs(idef, md, EI_DYNAMICS(ir->eI), cr, constr->lincsd);
 +        }
 +        if (ir->eConstrAlg == econtSHAKE)
 +        {
 +            if (cr->dd)
 +            {
 +                make_shake_sblock_dd(constr, &idef->il[F_CONSTR], &top->cgs, cr->dd);
 +            }
 +            else
 +            {
 +                make_shake_sblock_serial(constr, idef, md);
 +            }
 +            if (ncons > constr->lagr_nalloc)
 +            {
 +                constr->lagr_nalloc = over_alloc_dd(ncons);
 +                srenew(constr->lagr, constr->lagr_nalloc);
 +            }
 +        }
 +    }
 +
 +    if (idef->il[F_SETTLE].nr > 0 && constr->settled == NULL)
 +    {
 +        settle          = &idef->il[F_SETTLE];
 +        iO              = settle->iatoms[1];
 +        iH              = settle->iatoms[2];
 +        constr->settled =
 +            settle_init(md->massT[iO], md->massT[iH],
 +                        md->invmass[iO], md->invmass[iH],
 +                        idef->iparams[settle->iatoms[0]].settle.doh,
 +                        idef->iparams[settle->iatoms[0]].settle.dhh);
 +    }
 +
 +    /* Make a selection of the local atoms for essential dynamics */
 +    if (constr->ed && cr->dd)
 +    {
 +        dd_make_local_ed_indices(cr->dd, constr->ed);
 +    }
 +}
 +
 +static void constr_recur(t_blocka *at2con,
 +                         t_ilist *ilist, t_iparams *iparams, gmx_bool bTopB,
 +                         int at, int depth, int nc, int *path,
 +                         real r0, real r1, real *r2max,
 +                         int *count)
 +{
 +    int      ncon1;
 +    t_iatom *ia1, *ia2;
 +    int      c, con, a1;
 +    gmx_bool bUse;
 +    t_iatom *ia;
 +    real     len, rn0, rn1;
 +
 +    (*count)++;
 +
 +    ncon1 = ilist[F_CONSTR].nr/3;
 +    ia1   = ilist[F_CONSTR].iatoms;
 +    ia2   = ilist[F_CONSTRNC].iatoms;
 +
 +    /* Loop over all constraints connected to this atom */
 +    for (c = at2con->index[at]; c < at2con->index[at+1]; c++)
 +    {
 +        con = at2con->a[c];
 +        /* Do not walk over already used constraints */
 +        bUse = TRUE;
 +        for (a1 = 0; a1 < depth; a1++)
 +        {
 +            if (con == path[a1])
 +            {
 +                bUse = FALSE;
 +            }
 +        }
 +        if (bUse)
 +        {
 +            ia = constr_iatomptr(ncon1, ia1, ia2, con);
 +            /* Flexible constraints currently have length 0, which is incorrect */
 +            if (!bTopB)
 +            {
 +                len = iparams[ia[0]].constr.dA;
 +            }
 +            else
 +            {
 +                len = iparams[ia[0]].constr.dB;
 +            }
 +            /* In the worst case the bond directions alternate */
 +            if (nc % 2 == 0)
 +            {
 +                rn0 = r0 + len;
 +                rn1 = r1;
 +            }
 +            else
 +            {
 +                rn0 = r0;
 +                rn1 = r1 + len;
 +            }
 +            /* Assume angles of 120 degrees between all bonds */
 +            if (rn0*rn0 + rn1*rn1 + rn0*rn1 > *r2max)
 +            {
 +                *r2max = rn0*rn0 + rn1*rn1 + r0*rn1;
 +                if (debug)
 +                {
 +                    fprintf(debug, "Found longer constraint distance: r0 %5.3f r1 %5.3f rmax %5.3f\n", rn0, rn1, sqrt(*r2max));
 +                    for (a1 = 0; a1 < depth; a1++)
 +                    {
 +                        fprintf(debug, " %d %5.3f",
 +                                path[a1],
 +                                iparams[constr_iatomptr(ncon1, ia1, ia2, con)[0]].constr.dA);
 +                    }
 +                    fprintf(debug, " %d %5.3f\n", con, len);
 +                }
 +            }
 +            /* Limit the number of recursions to 1000*nc,
 +             * so a call does not take more than a second,
 +             * even for highly connected systems.
 +             */
 +            if (depth + 1 < nc && *count < 1000*nc)
 +            {
 +                if (ia[1] == at)
 +                {
 +                    a1 = ia[2];
 +                }
 +                else
 +                {
 +                    a1 = ia[1];
 +                }
 +                /* Recursion */
 +                path[depth] = con;
 +                constr_recur(at2con, ilist, iparams,
 +                             bTopB, a1, depth+1, nc, path, rn0, rn1, r2max, count);
 +                path[depth] = -1;
 +            }
 +        }
 +    }
 +}
 +
 +static real constr_r_max_moltype(gmx_moltype_t *molt, t_iparams *iparams,
 +                                 t_inputrec *ir)
 +{
 +    int      natoms, nflexcon, *path, at, count;
 +
 +    t_blocka at2con;
 +    real     r0, r1, r2maxA, r2maxB, rmax, lam0, lam1;
 +
 +    if (molt->ilist[F_CONSTR].nr   == 0 &&
 +        molt->ilist[F_CONSTRNC].nr == 0)
 +    {
 +        return 0;
 +    }
 +
 +    natoms = molt->atoms.nr;
 +
 +    at2con = make_at2con(0, natoms, molt->ilist, iparams,
 +                         EI_DYNAMICS(ir->eI), &nflexcon);
 +    snew(path, 1+ir->nProjOrder);
 +    for (at = 0; at < 1+ir->nProjOrder; at++)
 +    {
 +        path[at] = -1;
 +    }
 +
 +    r2maxA = 0;
 +    for (at = 0; at < natoms; at++)
 +    {
 +        r0 = 0;
 +        r1 = 0;
 +
 +        count = 0;
 +        constr_recur(&at2con, molt->ilist, iparams,
 +                     FALSE, at, 0, 1+ir->nProjOrder, path, r0, r1, &r2maxA, &count);
 +    }
 +    if (ir->efep == efepNO)
 +    {
 +        rmax = sqrt(r2maxA);
 +    }
 +    else
 +    {
 +        r2maxB = 0;
 +        for (at = 0; at < natoms; at++)
 +        {
 +            r0    = 0;
 +            r1    = 0;
 +            count = 0;
 +            constr_recur(&at2con, molt->ilist, iparams,
 +                         TRUE, at, 0, 1+ir->nProjOrder, path, r0, r1, &r2maxB, &count);
 +        }
 +        lam0 = ir->fepvals->init_lambda;
 +        if (EI_DYNAMICS(ir->eI))
 +        {
 +            lam0 += ir->init_step*ir->fepvals->delta_lambda;
 +        }
 +        rmax = (1 - lam0)*sqrt(r2maxA) + lam0*sqrt(r2maxB);
 +        if (EI_DYNAMICS(ir->eI))
 +        {
 +            lam1 = ir->fepvals->init_lambda + (ir->init_step + ir->nsteps)*ir->fepvals->delta_lambda;
 +            rmax = max(rmax, (1 - lam1)*sqrt(r2maxA) + lam1*sqrt(r2maxB));
 +        }
 +    }
 +
 +    done_blocka(&at2con);
 +    sfree(path);
 +
 +    return rmax;
 +}
 +
 +real constr_r_max(FILE *fplog, gmx_mtop_t *mtop, t_inputrec *ir)
 +{
 +    int  mt;
 +    real rmax;
 +
 +    rmax = 0;
 +    for (mt = 0; mt < mtop->nmoltype; mt++)
 +    {
 +        rmax = max(rmax,
 +                   constr_r_max_moltype(&mtop->moltype[mt],
 +                                        mtop->ffparams.iparams, ir));
 +    }
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Maximum distance for %d constraints, at 120 deg. angles, all-trans: %.3f nm\n", 1+ir->nProjOrder, rmax);
 +    }
 +
 +    return rmax;
 +}
 +
 +gmx_constr_t init_constraints(FILE *fplog,
 +                              gmx_mtop_t *mtop, t_inputrec *ir,
 +                              gmx_edsam_t ed, t_state *state,
 +                              t_commrec *cr)
 +{
 +    int                  ncon, nset, nmol, settle_type, i, natoms, mt, nflexcon;
 +    struct gmx_constr   *constr;
 +    char                *env;
 +    t_ilist             *ilist;
 +    gmx_mtop_ilistloop_t iloop;
 +
 +    ncon =
 +        gmx_mtop_ftype_count(mtop, F_CONSTR) +
 +        gmx_mtop_ftype_count(mtop, F_CONSTRNC);
 +    nset = gmx_mtop_ftype_count(mtop, F_SETTLE);
 +
 +    if (ncon+nset == 0 && ir->ePull != epullCONSTRAINT && ed == NULL)
 +    {
 +        return NULL;
 +    }
 +
 +    snew(constr, 1);
 +
 +    constr->ncon_tot = ncon;
 +    constr->nflexcon = 0;
 +    if (ncon > 0)
 +    {
 +        constr->n_at2con_mt = mtop->nmoltype;
 +        snew(constr->at2con_mt, constr->n_at2con_mt);
 +        for (mt = 0; mt < mtop->nmoltype; mt++)
 +        {
 +            constr->at2con_mt[mt] = make_at2con(0, mtop->moltype[mt].atoms.nr,
 +                                                mtop->moltype[mt].ilist,
 +                                                mtop->ffparams.iparams,
 +                                                EI_DYNAMICS(ir->eI), &nflexcon);
 +            for (i = 0; i < mtop->nmolblock; i++)
 +            {
 +                if (mtop->molblock[i].type == mt)
 +                {
 +                    constr->nflexcon += mtop->molblock[i].nmol*nflexcon;
 +                }
 +            }
 +        }
 +
 +        if (constr->nflexcon > 0)
 +        {
 +            if (fplog)
 +            {
 +                fprintf(fplog, "There are %d flexible constraints\n",
 +                        constr->nflexcon);
 +                if (ir->fc_stepsize == 0)
 +                {
 +                    fprintf(fplog, "\n"
 +                            "WARNING: step size for flexible constraining = 0\n"
 +                            "         All flexible constraints will be rigid.\n"
 +                            "         Will try to keep all flexible constraints at their original length,\n"
 +                            "         but the lengths may exhibit some drift.\n\n");
 +                    constr->nflexcon = 0;
 +                }
 +            }
 +            if (constr->nflexcon > 0)
 +            {
 +                please_cite(fplog, "Hess2002");
 +            }
 +        }
 +
 +        if (ir->eConstrAlg == econtLINCS)
 +        {
 +            constr->lincsd = init_lincs(fplog, mtop,
 +                                        constr->nflexcon, constr->at2con_mt,
 +                                        DOMAINDECOMP(cr) && cr->dd->bInterCGcons,
 +                                        ir->nLincsIter, ir->nProjOrder);
 +        }
 +
 +        if (ir->eConstrAlg == econtSHAKE)
 +        {
 +            if (DOMAINDECOMP(cr) && cr->dd->bInterCGcons)
 +            {
 +                gmx_fatal(FARGS, "SHAKE is not supported with domain decomposition and constraint that cross charge group boundaries, use LINCS");
 +            }
 +            if (constr->nflexcon)
 +            {
 +                gmx_fatal(FARGS, "For this system also velocities and/or forces need to be constrained, this can not be done with SHAKE, you should select LINCS");
 +            }
 +            please_cite(fplog, "Ryckaert77a");
 +            if (ir->bShakeSOR)
 +            {
 +                please_cite(fplog, "Barth95a");
 +            }
 +
 +            constr->shaked = shake_init();
 +        }
 +    }
 +
 +    if (nset > 0)
 +    {
 +        please_cite(fplog, "Miyamoto92a");
 +
 +        constr->bInterCGsettles = inter_charge_group_settles(mtop);
 +
 +        /* Check that we have only one settle type */
 +        settle_type = -1;
 +        iloop       = gmx_mtop_ilistloop_init(mtop);
 +        while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol))
 +        {
 +            for (i = 0; i < ilist[F_SETTLE].nr; i += 4)
 +            {
 +                if (settle_type == -1)
 +                {
 +                    settle_type = ilist[F_SETTLE].iatoms[i];
 +                }
 +                else if (ilist[F_SETTLE].iatoms[i] != settle_type)
 +                {
 +                    gmx_fatal(FARGS,
 +                              "The [molecules] section of your topology specifies more than one block of\n"
 +                              "a [moleculetype] with a [settles] block. Only one such is allowed. If you\n"
 +                              "are trying to partition your solvent into different *groups* (e.g. for\n"
 +                              "freezing, T-coupling, etc.) then you are using the wrong approach. Index\n"
 +                              "files specify groups. Otherwise, you may wish to change the least-used\n"
 +                              "block of molecules with SETTLE constraints into 3 normal constraints.");
 +                }
 +            }
 +        }
 +
 +        constr->n_at2settle_mt = mtop->nmoltype;
 +        snew(constr->at2settle_mt, constr->n_at2settle_mt);
 +        for (mt = 0; mt < mtop->nmoltype; mt++)
 +        {
 +            constr->at2settle_mt[mt] =
 +                make_at2settle(mtop->moltype[mt].atoms.nr,
 +                               &mtop->moltype[mt].ilist[F_SETTLE]);
 +        }
 +    }
 +
 +    constr->maxwarn = 999;
 +    env             = getenv("GMX_MAXCONSTRWARN");
 +    if (env)
 +    {
 +        constr->maxwarn = 0;
 +        sscanf(env, "%d", &constr->maxwarn);
 +        if (fplog)
 +        {
 +            fprintf(fplog,
 +                    "Setting the maximum number of constraint warnings to %d\n",
 +                    constr->maxwarn);
 +        }
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr,
 +                    "Setting the maximum number of constraint warnings to %d\n",
 +                    constr->maxwarn);
 +        }
 +    }
 +    if (constr->maxwarn < 0 && fplog)
 +    {
 +        fprintf(fplog, "maxwarn < 0, will not stop on constraint errors\n");
 +    }
 +    constr->warncount_lincs  = 0;
 +    constr->warncount_settle = 0;
 +
 +    /* Initialize the essential dynamics sampling.
 +     * Put the pointer to the ED struct in constr */
 +    constr->ed = ed;
 +    if (ed != NULL || state->edsamstate.nED > 0)
 +    {
 +        init_edsam(mtop, ir, cr, ed, state->x, state->box, &state->edsamstate);
 +    }
 +
 +    constr->warn_mtop = mtop;
 +
 +    return constr;
 +}
 +
 +const t_blocka *atom2constraints_moltype(gmx_constr_t constr)
 +{
 +    return constr->at2con_mt;
 +}
 +
 +const int **atom2settle_moltype(gmx_constr_t constr)
 +{
 +    return (const int **)constr->at2settle_mt;
 +}
 +
 +
 +gmx_bool inter_charge_group_constraints(const gmx_mtop_t *mtop)
 +{
 +    const gmx_moltype_t *molt;
 +    const t_block       *cgs;
 +    const t_ilist       *il;
 +    int                  mb;
 +    int                  nat, *at2cg, cg, a, ftype, i;
 +    gmx_bool             bInterCG;
 +
 +    bInterCG = FALSE;
 +    for (mb = 0; mb < mtop->nmolblock && !bInterCG; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +
 +        if (molt->ilist[F_CONSTR].nr   > 0 ||
 +            molt->ilist[F_CONSTRNC].nr > 0 ||
 +            molt->ilist[F_SETTLE].nr > 0)
 +        {
 +            cgs  = &molt->cgs;
 +            snew(at2cg, molt->atoms.nr);
 +            for (cg = 0; cg < cgs->nr; cg++)
 +            {
 +                for (a = cgs->index[cg]; a < cgs->index[cg+1]; a++)
 +                {
 +                    at2cg[a] = cg;
 +                }
 +            }
 +
 +            for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
 +            {
 +                il = &molt->ilist[ftype];
 +                for (i = 0; i < il->nr && !bInterCG; i += 1+NRAL(ftype))
 +                {
 +                    if (at2cg[il->iatoms[i+1]] != at2cg[il->iatoms[i+2]])
 +                    {
 +                        bInterCG = TRUE;
 +                    }
 +                }
 +            }
 +
 +            sfree(at2cg);
 +        }
 +    }
 +
 +    return bInterCG;
 +}
 +
 +gmx_bool inter_charge_group_settles(const gmx_mtop_t *mtop)
 +{
 +    const gmx_moltype_t *molt;
 +    const t_block       *cgs;
 +    const t_ilist       *il;
 +    int                  mb;
 +    int                  nat, *at2cg, cg, a, ftype, i;
 +    gmx_bool             bInterCG;
 +
 +    bInterCG = FALSE;
 +    for (mb = 0; mb < mtop->nmolblock && !bInterCG; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +
 +        if (molt->ilist[F_SETTLE].nr > 0)
 +        {
 +            cgs  = &molt->cgs;
 +            snew(at2cg, molt->atoms.nr);
 +            for (cg = 0; cg < cgs->nr; cg++)
 +            {
 +                for (a = cgs->index[cg]; a < cgs->index[cg+1]; a++)
 +                {
 +                    at2cg[a] = cg;
 +                }
 +            }
 +
 +            for (ftype = F_SETTLE; ftype <= F_SETTLE; ftype++)
 +            {
 +                il = &molt->ilist[ftype];
 +                for (i = 0; i < il->nr && !bInterCG; i += 1+NRAL(F_SETTLE))
 +                {
 +                    if (at2cg[il->iatoms[i+1]] != at2cg[il->iatoms[i+2]] ||
 +                        at2cg[il->iatoms[i+1]] != at2cg[il->iatoms[i+3]])
 +                    {
 +                        bInterCG = TRUE;
 +                    }
 +                }
 +            }
 +
 +            sfree(at2cg);
 +        }
 +    }
 +
 +    return bInterCG;
 +}
 +
 +/* helper functions for andersen temperature control, because the
 + * gmx_constr construct is only defined in constr.c. Return the list
 + * of blocks (get_sblock) and the number of blocks (get_nblocks).  */
 +
 +extern int *get_sblock(struct gmx_constr *constr)
 +{
 +    return constr->sblock;
 +}
 +
 +extern int get_nblocks(struct gmx_constr *constr)
 +{
 +    return constr->nblocks;
 +}
index 31154f34609ab4b81e02b576cd0a16a8694dd45c,0000000000000000000000000000000000000000..417992bfc9e17703215d9aef190700445cf54314
mode 100644,000000..100644
--- /dev/null
@@@ -1,1408 -1,0 +1,1411 @@@
-                              matrix box, rvec *x0, rvec *x1)
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2006,2007,2008,2009,2010,2012,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.
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <assert.h>
 +
 +#include "smalloc.h"
 +#include "vec.h"
 +#include "constr.h"
 +#include "types/commrec.h"
 +#include "domdec.h"
 +#include "domdec_network.h"
 +#include "mtop_util.h"
 +#include "gmx_ga2la.h"
 +#include "gmx_hash.h"
 +#include "gmx_omp_nthreads.h"
 +#include "macros.h"
 +
 +typedef struct {
 +    int  nsend;
 +    int *a;
 +    int  a_nalloc;
 +    int  nrecv;
 +} gmx_specatsend_t;
 +
 +typedef struct {
 +    int *ind;
 +    int  nalloc;
 +    int  n;
 +} ind_req_t;
 +
 +typedef struct gmx_domdec_specat_comm {
 +    /* The number of indices to receive during the setup */
 +    int              nreq[DIM][2][2];
 +    /* The atoms to send */
 +    gmx_specatsend_t spas[DIM][2];
 +    gmx_bool        *bSendAtom;
 +    int              bSendAtom_nalloc;
 +    /* Send buffers */
 +    int             *ibuf;
 +    int              ibuf_nalloc;
 +    rvec            *vbuf;
 +    int              vbuf_nalloc;
 +    rvec            *vbuf2;
 +    int              vbuf2_nalloc;
 +    /* The range in the local buffer(s) for received atoms */
 +    int              at_start;
 +    int              at_end;
 +
 +    /* The atom indices we need from the surrounding cells.
 +     * We can gather the indices over nthread threads.
 +     */
 +    int        nthread;
 +    ind_req_t *ireq;
 +} gmx_domdec_specat_comm_t;
 +
 +typedef struct gmx_domdec_constraints {
 +    int       *molb_con_offset;
 +    int       *molb_ncon_mol;
 +    /* The fully local and connected constraints */
 +    int        ncon;
 +    /* The global constraint number, only required for clearing gc_req */
 +    int       *con_gl;
 +    int       *con_nlocat;
 +    int        con_nalloc;
 +    /* Boolean that tells if a global constraint index has been requested */
 +    char      *gc_req;
 +    /* Global to local communicated constraint atom only index */
 +    gmx_hash_t ga2la;
 +
 +    /* Multi-threading stuff */
 +    int      nthread;
 +    t_ilist *ils;
 +} gmx_domdec_constraints_t;
 +
 +
 +static void dd_move_f_specat(gmx_domdec_t *dd, gmx_domdec_specat_comm_t *spac,
 +                             rvec *f, rvec *fshift)
 +{
 +    gmx_specatsend_t *spas;
 +    rvec             *vbuf;
 +    int               n, n0, n1, d, dim, dir, i;
 +    ivec              vis;
 +    int               is;
 +    gmx_bool          bPBC, bScrew;
 +
 +    n = spac->at_end;
 +    for (d = dd->ndim-1; d >= 0; d--)
 +    {
 +        dim = dd->dim[d];
 +        if (dd->nc[dim] > 2)
 +        {
 +            /* Pulse the grid forward and backward */
 +            spas = spac->spas[d];
 +            n0   = spas[0].nrecv;
 +            n1   = spas[1].nrecv;
 +            n   -= n1 + n0;
 +            vbuf = spac->vbuf;
 +            /* Send and receive the coordinates */
 +            dd_sendrecv2_rvec(dd, d,
 +                              f+n+n1, n0, vbuf, spas[0].nsend,
 +                              f+n, n1, vbuf+spas[0].nsend, spas[1].nsend);
 +            for (dir = 0; dir < 2; dir++)
 +            {
 +                bPBC   = ((dir == 0 && dd->ci[dim] == 0) ||
 +                          (dir == 1 && dd->ci[dim] == dd->nc[dim]-1));
 +                bScrew = (bPBC && dd->bScrewPBC && dim == XX);
 +
 +                spas = &spac->spas[d][dir];
 +                /* Sum the buffer into the required forces */
 +                if (!bPBC || (!bScrew && fshift == NULL))
 +                {
 +                    for (i = 0; i < spas->nsend; i++)
 +                    {
 +                        rvec_inc(f[spas->a[i]], *vbuf);
 +                        vbuf++;
 +                    }
 +                }
 +                else
 +                {
 +                    clear_ivec(vis);
 +                    vis[dim] = (dir == 0 ? 1 : -1);
 +                    is       = IVEC2IS(vis);
 +                    if (!bScrew)
 +                    {
 +                        /* Sum and add to shift forces */
 +                        for (i = 0; i < spas->nsend; i++)
 +                        {
 +                            rvec_inc(f[spas->a[i]], *vbuf);
 +                            rvec_inc(fshift[is], *vbuf);
 +                            vbuf++;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        /* Rotate the forces */
 +                        for (i = 0; i < spas->nsend; i++)
 +                        {
 +                            f[spas->a[i]][XX] += (*vbuf)[XX];
 +                            f[spas->a[i]][YY] -= (*vbuf)[YY];
 +                            f[spas->a[i]][ZZ] -= (*vbuf)[ZZ];
 +                            if (fshift)
 +                            {
 +                                rvec_inc(fshift[is], *vbuf);
 +                            }
 +                            vbuf++;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            /* Two cells, so we only need to communicate one way */
 +            spas = &spac->spas[d][0];
 +            n   -= spas->nrecv;
 +            /* Send and receive the coordinates */
 +            dd_sendrecv_rvec(dd, d, dddirForward,
 +                             f+n, spas->nrecv, spac->vbuf, spas->nsend);
 +            /* Sum the buffer into the required forces */
 +            if (dd->bScrewPBC && dim == XX &&
 +                (dd->ci[dim] == 0 ||
 +                 dd->ci[dim] == dd->nc[dim]-1))
 +            {
 +                for (i = 0; i < spas->nsend; i++)
 +                {
 +                    /* Rotate the force */
 +                    f[spas->a[i]][XX] += spac->vbuf[i][XX];
 +                    f[spas->a[i]][YY] -= spac->vbuf[i][YY];
 +                    f[spas->a[i]][ZZ] -= spac->vbuf[i][ZZ];
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < spas->nsend; i++)
 +                {
 +                    rvec_inc(f[spas->a[i]], spac->vbuf[i]);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void dd_move_f_vsites(gmx_domdec_t *dd, rvec *f, rvec *fshift)
 +{
 +    if (dd->vsite_comm)
 +    {
 +        dd_move_f_specat(dd, dd->vsite_comm, f, fshift);
 +    }
 +}
 +
 +void dd_clear_f_vsites(gmx_domdec_t *dd, rvec *f)
 +{
 +    int i;
 +
 +    if (dd->vsite_comm)
 +    {
 +        for (i = dd->vsite_comm->at_start; i < dd->vsite_comm->at_end; i++)
 +        {
 +            clear_rvec(f[i]);
 +        }
 +    }
 +}
 +
 +static void dd_move_x_specat(gmx_domdec_t *dd, gmx_domdec_specat_comm_t *spac,
-     if (x1)
++                             matrix box,
++                             rvec *x0,
++                             rvec *x1, gmx_bool bX1IsCoord)
 +{
 +    gmx_specatsend_t *spas;
 +    rvec             *x, *vbuf, *rbuf;
 +    int               nvec, v, n, nn, ns0, ns1, nr0, nr1, nr, d, dim, dir, i;
 +    gmx_bool          bPBC, bScrew = FALSE;
 +    rvec              shift = {0, 0, 0};
 +
 +    nvec = 1;
-                     if (!bPBC)
++    if (x1 != NULL)
 +    {
 +        nvec++;
 +    }
 +
 +    n = spac->at_start;
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim = dd->dim[d];
 +        if (dd->nc[dim] > 2)
 +        {
 +            /* Pulse the grid forward and backward */
 +            vbuf = spac->vbuf;
 +            for (dir = 0; dir < 2; dir++)
 +            {
 +                if (dir == 0 && dd->ci[dim] == 0)
 +                {
 +                    bPBC   = TRUE;
 +                    bScrew = (dd->bScrewPBC && dim == XX);
 +                    copy_rvec(box[dim], shift);
 +                }
 +                else if (dir == 1 && dd->ci[dim] == dd->nc[dim]-1)
 +                {
 +                    bPBC   = TRUE;
 +                    bScrew = (dd->bScrewPBC && dim == XX);
 +                    for (i = 0; i < DIM; i++)
 +                    {
 +                        shift[i] = -box[dim][i];
 +                    }
 +                }
 +                else
 +                {
 +                    bPBC   = FALSE;
 +                    bScrew = FALSE;
 +                }
 +                spas = &spac->spas[d][dir];
 +                for (v = 0; v < nvec; v++)
 +                {
 +                    x = (v == 0 ? x0 : x1);
 +                    /* Copy the required coordinates to the send buffer */
- void dd_move_x_constraints(gmx_domdec_t *dd, matrix box, rvec *x0, rvec *x1)
++                    if (!bPBC || (v == 1 && !bX1IsCoord))
 +                    {
 +                        /* Only copy */
 +                        for (i = 0; i < spas->nsend; i++)
 +                        {
 +                            copy_rvec(x[spas->a[i]], *vbuf);
 +                            vbuf++;
 +                        }
 +                    }
 +                    else if (!bScrew)
 +                    {
 +                        /* Shift coordinates */
 +                        for (i = 0; i < spas->nsend; i++)
 +                        {
 +                            rvec_add(x[spas->a[i]], shift, *vbuf);
 +                            vbuf++;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        /* Shift and rotate coordinates */
 +                        for (i = 0; i < spas->nsend; i++)
 +                        {
 +                            (*vbuf)[XX] =               x[spas->a[i]][XX] + shift[XX];
 +                            (*vbuf)[YY] = box[YY][YY] - x[spas->a[i]][YY] + shift[YY];
 +                            (*vbuf)[ZZ] = box[ZZ][ZZ] - x[spas->a[i]][ZZ] + shift[ZZ];
 +                            vbuf++;
 +                        }
 +                    }
 +                }
 +            }
 +            /* Send and receive the coordinates */
 +            spas = spac->spas[d];
 +            ns0  = spas[0].nsend;
 +            nr0  = spas[0].nrecv;
 +            ns1  = spas[1].nsend;
 +            nr1  = spas[1].nrecv;
 +            if (nvec == 1)
 +            {
 +                dd_sendrecv2_rvec(dd, d,
 +                                  spac->vbuf+ns0, ns1, x0+n, nr1,
 +                                  spac->vbuf, ns0, x0+n+nr1, nr0);
 +            }
 +            else
 +            {
 +                /* Communicate both vectors in one buffer */
 +                rbuf = spac->vbuf2;
 +                dd_sendrecv2_rvec(dd, d,
 +                                  spac->vbuf+2*ns0, 2*ns1, rbuf, 2*nr1,
 +                                  spac->vbuf, 2*ns0, rbuf+2*nr1, 2*nr0);
 +                /* Split the buffer into the two vectors */
 +                nn = n;
 +                for (dir = 1; dir >= 0; dir--)
 +                {
 +                    nr = spas[dir].nrecv;
 +                    for (v = 0; v < 2; v++)
 +                    {
 +                        x = (v == 0 ? x0 : x1);
 +                        for (i = 0; i < nr; i++)
 +                        {
 +                            copy_rvec(*rbuf, x[nn+i]);
 +                            rbuf++;
 +                        }
 +                    }
 +                    nn += nr;
 +                }
 +            }
 +            n += nr0 + nr1;
 +        }
 +        else
 +        {
 +            spas = &spac->spas[d][0];
 +            /* Copy the required coordinates to the send buffer */
 +            vbuf = spac->vbuf;
 +            for (v = 0; v < nvec; v++)
 +            {
 +                x = (v == 0 ? x0 : x1);
 +                if (dd->bScrewPBC && dim == XX &&
 +                    (dd->ci[XX] == 0 || dd->ci[XX] == dd->nc[XX]-1))
 +                {
 +                    /* Here we only perform the rotation, the rest of the pbc
 +                     * is handled in the constraint or viste routines.
 +                     */
 +                    for (i = 0; i < spas->nsend; i++)
 +                    {
 +                        (*vbuf)[XX] =               x[spas->a[i]][XX];
 +                        (*vbuf)[YY] = box[YY][YY] - x[spas->a[i]][YY];
 +                        (*vbuf)[ZZ] = box[ZZ][ZZ] - x[spas->a[i]][ZZ];
 +                        vbuf++;
 +                    }
 +                }
 +                else
 +                {
 +                    for (i = 0; i < spas->nsend; i++)
 +                    {
 +                        copy_rvec(x[spas->a[i]], *vbuf);
 +                        vbuf++;
 +                    }
 +                }
 +            }
 +            /* Send and receive the coordinates */
 +            if (nvec == 1)
 +            {
 +                dd_sendrecv_rvec(dd, d, dddirBackward,
 +                                 spac->vbuf, spas->nsend, x0+n, spas->nrecv);
 +            }
 +            else
 +            {
 +                /* Communicate both vectors in one buffer */
 +                rbuf = spac->vbuf2;
 +                dd_sendrecv_rvec(dd, d, dddirBackward,
 +                                 spac->vbuf, 2*spas->nsend, rbuf, 2*spas->nrecv);
 +                /* Split the buffer into the two vectors */
 +                nr = spas[0].nrecv;
 +                for (v = 0; v < 2; v++)
 +                {
 +                    x = (v == 0 ? x0 : x1);
 +                    for (i = 0; i < nr; i++)
 +                    {
 +                        copy_rvec(*rbuf, x[n+i]);
 +                        rbuf++;
 +                    }
 +                }
 +            }
 +            n += spas->nrecv;
 +        }
 +    }
 +}
 +
-         dd_move_x_specat(dd, dd->constraint_comm, box, x0, x1);
++void dd_move_x_constraints(gmx_domdec_t *dd, matrix box,
++                           rvec *x0, rvec *x1, gmx_bool bX1IsCoord)
 +{
 +    if (dd->constraint_comm)
 +    {
-         dd_move_x_specat(dd, dd->vsite_comm, box, x, NULL);
++        dd_move_x_specat(dd, dd->constraint_comm, box, x0, x1, bX1IsCoord);
 +    }
 +}
 +
 +void dd_move_x_vsites(gmx_domdec_t *dd, matrix box, rvec *x)
 +{
 +    if (dd->vsite_comm)
 +    {
++        dd_move_x_specat(dd, dd->vsite_comm, box, x, NULL, FALSE);
 +    }
 +}
 +
 +int *dd_constraints_nlocalatoms(gmx_domdec_t *dd)
 +{
 +    if (dd->constraints)
 +    {
 +        return dd->constraints->con_nlocat;
 +    }
 +    else
 +    {
 +        return NULL;
 +    }
 +}
 +
 +void dd_clear_local_constraint_indices(gmx_domdec_t *dd)
 +{
 +    gmx_domdec_constraints_t *dc;
 +    int i;
 +
 +    dc = dd->constraints;
 +
 +    for (i = 0; i < dc->ncon; i++)
 +    {
 +        dc->gc_req[dc->con_gl[i]] = 0;
 +    }
 +
 +    if (dd->constraint_comm)
 +    {
 +        gmx_hash_clear_and_optimize(dc->ga2la);
 +    }
 +}
 +
 +void dd_clear_local_vsite_indices(gmx_domdec_t *dd)
 +{
 +    int i;
 +
 +    if (dd->vsite_comm)
 +    {
 +        gmx_hash_clear_and_optimize(dd->ga2la_vsite);
 +    }
 +}
 +
 +static int setup_specat_communication(gmx_domdec_t             *dd,
 +                                      ind_req_t                *ireq,
 +                                      gmx_domdec_specat_comm_t *spac,
 +                                      gmx_hash_t                ga2la_specat,
 +                                      int                       at_start,
 +                                      int                       vbuf_fac,
 +                                      const char               *specat_type,
 +                                      const char               *add_err)
 +{
 +    int               nsend[2], nlast, nsend_zero[2] = {0, 0}, *nsend_ptr;
 +    int               d, dim, ndir, dir, nr, ns, i, nrecv_local, n0, start, indr, ind, buf[2];
 +    int               nat_tot_specat, nat_tot_prev, nalloc_old;
 +    gmx_bool          bPBC, bFirst;
 +    gmx_specatsend_t *spas;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Begin setup_specat_communication for %s\n", specat_type);
 +    }
 +
 +    /* nsend[0]: the number of atoms requested by this node only,
 +     *           we communicate this for more efficients checks
 +     * nsend[1]: the total number of requested atoms
 +     */
 +    nsend[0] = ireq->n;
 +    nsend[1] = nsend[0];
 +    nlast    = nsend[1];
 +    for (d = dd->ndim-1; d >= 0; d--)
 +    {
 +        /* Pulse the grid forward and backward */
 +        dim  = dd->dim[d];
 +        bPBC = (dim < dd->npbcdim);
 +        if (dd->nc[dim] == 2)
 +        {
 +            /* Only 2 cells, so we only need to communicate once */
 +            ndir = 1;
 +        }
 +        else
 +        {
 +            ndir = 2;
 +        }
 +        for (dir = 0; dir < ndir; dir++)
 +        {
 +            if (!bPBC &&
 +                dd->nc[dim] > 2 &&
 +                ((dir == 0 && dd->ci[dim] == dd->nc[dim] - 1) ||
 +                 (dir == 1 && dd->ci[dim] == 0)))
 +            {
 +                /* No pbc: the fist/last cell should not request atoms */
 +                nsend_ptr = nsend_zero;
 +            }
 +            else
 +            {
 +                nsend_ptr = nsend;
 +            }
 +            /* Communicate the number of indices */
 +            dd_sendrecv_int(dd, d, dir == 0 ? dddirForward : dddirBackward,
 +                            nsend_ptr, 2, spac->nreq[d][dir], 2);
 +            nr = spac->nreq[d][dir][1];
 +            if (nlast+nr > ireq->nalloc)
 +            {
 +                ireq->nalloc = over_alloc_dd(nlast+nr);
 +                srenew(ireq->ind, ireq->nalloc);
 +            }
 +            /* Communicate the indices */
 +            dd_sendrecv_int(dd, d, dir == 0 ? dddirForward : dddirBackward,
 +                            ireq->ind, nsend_ptr[1], ireq->ind+nlast, nr);
 +            nlast += nr;
 +        }
 +        nsend[1] = nlast;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Communicated the counts\n");
 +    }
 +
 +    /* Search for the requested atoms and communicate the indices we have */
 +    nat_tot_specat = at_start;
 +    nrecv_local    = 0;
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        bFirst = (d == 0);
 +        /* Pulse the grid forward and backward */
 +        if (dd->dim[d] >= dd->npbcdim || dd->nc[dd->dim[d]] > 2)
 +        {
 +            ndir = 2;
 +        }
 +        else
 +        {
 +            ndir = 1;
 +        }
 +        nat_tot_prev = nat_tot_specat;
 +        for (dir = ndir-1; dir >= 0; dir--)
 +        {
 +            if (nat_tot_specat > spac->bSendAtom_nalloc)
 +            {
 +                nalloc_old             = spac->bSendAtom_nalloc;
 +                spac->bSendAtom_nalloc = over_alloc_dd(nat_tot_specat);
 +                srenew(spac->bSendAtom, spac->bSendAtom_nalloc);
 +                for (i = nalloc_old; i < spac->bSendAtom_nalloc; i++)
 +                {
 +                    spac->bSendAtom[i] = FALSE;
 +                }
 +            }
 +            spas = &spac->spas[d][dir];
 +            n0   = spac->nreq[d][dir][0];
 +            nr   = spac->nreq[d][dir][1];
 +            if (debug)
 +            {
 +                fprintf(debug, "dim=%d, dir=%d, searching for %d atoms\n",
 +                        d, dir, nr);
 +            }
 +            start       = nlast - nr;
 +            spas->nsend = 0;
 +            nsend[0]    = 0;
 +            for (i = 0; i < nr; i++)
 +            {
 +                indr = ireq->ind[start+i];
 +                ind  = -1;
 +                /* Check if this is a home atom and if so ind will be set */
 +                if (!ga2la_get_home(dd->ga2la, indr, &ind))
 +                {
 +                    /* Search in the communicated atoms */
 +                    ind = gmx_hash_get_minone(ga2la_specat, indr);
 +                }
 +                if (ind >= 0)
 +                {
 +                    if (i < n0 || !spac->bSendAtom[ind])
 +                    {
 +                        if (spas->nsend+1 > spas->a_nalloc)
 +                        {
 +                            spas->a_nalloc = over_alloc_large(spas->nsend+1);
 +                            srenew(spas->a, spas->a_nalloc);
 +                        }
 +                        /* Store the local index so we know which coordinates
 +                         * to send out later.
 +                         */
 +                        spas->a[spas->nsend] = ind;
 +                        spac->bSendAtom[ind] = TRUE;
 +                        if (spas->nsend+1 > spac->ibuf_nalloc)
 +                        {
 +                            spac->ibuf_nalloc = over_alloc_large(spas->nsend+1);
 +                            srenew(spac->ibuf, spac->ibuf_nalloc);
 +                        }
 +                        /* Store the global index so we can send it now */
 +                        spac->ibuf[spas->nsend] = indr;
 +                        if (i < n0)
 +                        {
 +                            nsend[0]++;
 +                        }
 +                        spas->nsend++;
 +                    }
 +                }
 +            }
 +            nlast = start;
 +            /* Clear the local flags */
 +            for (i = 0; i < spas->nsend; i++)
 +            {
 +                spac->bSendAtom[spas->a[i]] = FALSE;
 +            }
 +            /* Send and receive the number of indices to communicate */
 +            nsend[1] = spas->nsend;
 +            dd_sendrecv_int(dd, d, dir == 0 ? dddirBackward : dddirForward,
 +                            nsend, 2, buf, 2);
 +            if (debug)
 +            {
 +                fprintf(debug, "Send to node %d, %d (%d) indices, "
 +                        "receive from node %d, %d (%d) indices\n",
 +                        dd->neighbor[d][1-dir], nsend[1], nsend[0],
 +                        dd->neighbor[d][dir], buf[1], buf[0]);
 +                if (gmx_debug_at)
 +                {
 +                    for (i = 0; i < spas->nsend; i++)
 +                    {
 +                        fprintf(debug, " %d", spac->ibuf[i]+1);
 +                    }
 +                    fprintf(debug, "\n");
 +                }
 +            }
 +            nrecv_local += buf[0];
 +            spas->nrecv  = buf[1];
 +            if (nat_tot_specat + spas->nrecv > dd->gatindex_nalloc)
 +            {
 +                dd->gatindex_nalloc =
 +                    over_alloc_dd(nat_tot_specat + spas->nrecv);
 +                srenew(dd->gatindex, dd->gatindex_nalloc);
 +            }
 +            /* Send and receive the indices */
 +            dd_sendrecv_int(dd, d, dir == 0 ? dddirBackward : dddirForward,
 +                            spac->ibuf, spas->nsend,
 +                            dd->gatindex+nat_tot_specat, spas->nrecv);
 +            nat_tot_specat += spas->nrecv;
 +        }
 +
 +        /* Allocate the x/f communication buffers */
 +        ns = spac->spas[d][0].nsend;
 +        nr = spac->spas[d][0].nrecv;
 +        if (ndir == 2)
 +        {
 +            ns += spac->spas[d][1].nsend;
 +            nr += spac->spas[d][1].nrecv;
 +        }
 +        if (vbuf_fac*ns > spac->vbuf_nalloc)
 +        {
 +            spac->vbuf_nalloc = over_alloc_dd(vbuf_fac*ns);
 +            srenew(spac->vbuf, spac->vbuf_nalloc);
 +        }
 +        if (vbuf_fac == 2 && vbuf_fac*nr > spac->vbuf2_nalloc)
 +        {
 +            spac->vbuf2_nalloc = over_alloc_dd(vbuf_fac*nr);
 +            srenew(spac->vbuf2, spac->vbuf2_nalloc);
 +        }
 +
 +        /* Make a global to local index for the communication atoms */
 +        for (i = nat_tot_prev; i < nat_tot_specat; i++)
 +        {
 +            gmx_hash_change_or_set(ga2la_specat, dd->gatindex[i], i);
 +        }
 +    }
 +
 +    /* Check that in the end we got the number of atoms we asked for */
 +    if (nrecv_local != ireq->n)
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "Requested %d, received %d (tot recv %d)\n",
 +                    ireq->n, nrecv_local, nat_tot_specat-at_start);
 +            if (gmx_debug_at)
 +            {
 +                for (i = 0; i < ireq->n; i++)
 +                {
 +                    ind = gmx_hash_get_minone(ga2la_specat, ireq->ind[i]);
 +                    fprintf(debug, " %s%d",
 +                            (ind >= 0) ? "" : "!",
 +                            ireq->ind[i]+1);
 +                }
 +                fprintf(debug, "\n");
 +            }
 +        }
 +        fprintf(stderr, "\nDD cell %d %d %d: Neighboring cells do not have atoms:",
 +                dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +        for (i = 0; i < ireq->n; i++)
 +        {
 +            if (gmx_hash_get_minone(ga2la_specat, ireq->ind[i]) < 0)
 +            {
 +                fprintf(stderr, " %d", ireq->ind[i]+1);
 +            }
 +        }
 +        fprintf(stderr, "\n");
 +        gmx_fatal(FARGS, "DD cell %d %d %d could only obtain %d of the %d atoms that are connected via %ss from the neighboring cells. This probably means your %s lengths are too long compared to the domain decomposition cell size. Decrease the number of domain decomposition grid cells%s%s.",
 +                  dd->ci[XX], dd->ci[YY], dd->ci[ZZ],
 +                  nrecv_local, ireq->n, specat_type,
 +                  specat_type, add_err,
 +                  dd->bGridJump ? " or use the -rcon option of mdrun" : "");
 +    }
 +
 +    spac->at_start = at_start;
 +    spac->at_end   = nat_tot_specat;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Done setup_specat_communication\n");
 +    }
 +
 +    return nat_tot_specat;
 +}
 +
 +static void walk_out(int con, int con_offset, int a, int offset, int nrec,
 +                     int ncon1, const t_iatom *ia1, const t_iatom *ia2,
 +                     const t_blocka *at2con,
 +                     const gmx_ga2la_t ga2la, gmx_bool bHomeConnect,
 +                     gmx_domdec_constraints_t *dc,
 +                     gmx_domdec_specat_comm_t *dcc,
 +                     t_ilist *il_local,
 +                     ind_req_t *ireq)
 +{
 +    int            a1_gl, a2_gl, a_loc, i, coni, b;
 +    const t_iatom *iap;
 +
 +    if (dc->gc_req[con_offset+con] == 0)
 +    {
 +        /* Add this non-home constraint to the list */
 +        if (dc->ncon+1 > dc->con_nalloc)
 +        {
 +            dc->con_nalloc = over_alloc_large(dc->ncon+1);
 +            srenew(dc->con_gl, dc->con_nalloc);
 +            srenew(dc->con_nlocat, dc->con_nalloc);
 +        }
 +        dc->con_gl[dc->ncon]       = con_offset + con;
 +        dc->con_nlocat[dc->ncon]   = (bHomeConnect ? 1 : 0);
 +        dc->gc_req[con_offset+con] = 1;
 +        if (il_local->nr + 3 > il_local->nalloc)
 +        {
 +            il_local->nalloc = over_alloc_dd(il_local->nr+3);
 +            srenew(il_local->iatoms, il_local->nalloc);
 +        }
 +        iap = constr_iatomptr(ncon1, ia1, ia2, con);
 +        il_local->iatoms[il_local->nr++] = iap[0];
 +        a1_gl = offset + iap[1];
 +        a2_gl = offset + iap[2];
 +        /* The following indexing code can probably be optizimed */
 +        if (ga2la_get_home(ga2la, a1_gl, &a_loc))
 +        {
 +            il_local->iatoms[il_local->nr++] = a_loc;
 +        }
 +        else
 +        {
 +            /* We set this index later */
 +            il_local->iatoms[il_local->nr++] = -a1_gl - 1;
 +        }
 +        if (ga2la_get_home(ga2la, a2_gl, &a_loc))
 +        {
 +            il_local->iatoms[il_local->nr++] = a_loc;
 +        }
 +        else
 +        {
 +            /* We set this index later */
 +            il_local->iatoms[il_local->nr++] = -a2_gl - 1;
 +        }
 +        dc->ncon++;
 +    }
 +    /* Check to not ask for the same atom more than once */
 +    if (gmx_hash_get_minone(dc->ga2la, offset+a) == -1)
 +    {
 +        assert(dcc);
 +        /* Add this non-home atom to the list */
 +        if (ireq->n+1 > ireq->nalloc)
 +        {
 +            ireq->nalloc = over_alloc_large(ireq->n+1);
 +            srenew(ireq->ind, ireq->nalloc);
 +        }
 +        ireq->ind[ireq->n++] = offset + a;
 +        /* Temporarily mark with -2, we get the index later */
 +        gmx_hash_set(dc->ga2la, offset+a, -2);
 +    }
 +
 +    if (nrec > 0)
 +    {
 +        for (i = at2con->index[a]; i < at2con->index[a+1]; i++)
 +        {
 +            coni = at2con->a[i];
 +            if (coni != con)
 +            {
 +                /* Walk further */
 +                iap = constr_iatomptr(ncon1, ia1, ia2, coni);
 +                if (a == iap[1])
 +                {
 +                    b = iap[2];
 +                }
 +                else
 +                {
 +                    b = iap[1];
 +                }
 +                if (!ga2la_get_home(ga2la, offset+b, &a_loc))
 +                {
 +                    walk_out(coni, con_offset, b, offset, nrec-1,
 +                             ncon1, ia1, ia2, at2con,
 +                             ga2la, FALSE, dc, dcc, il_local, ireq);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void atoms_to_settles(gmx_domdec_t *dd,
 +                             const gmx_mtop_t *mtop,
 +                             const int *cginfo,
 +                             const int **at2settle_mt,
 +                             int cg_start, int cg_end,
 +                             t_ilist *ils_local,
 +                             ind_req_t *ireq)
 +{
 +    gmx_ga2la_t           ga2la;
 +    gmx_mtop_atomlookup_t alook;
 +    int                   settle;
 +    int                   nral, sa;
 +    int                   cg, a, a_gl, a_glsa, a_gls[3], a_locs[3];
 +    int                   mb, molnr, a_mol, offset;
 +    const gmx_molblock_t *molb;
 +    const t_iatom        *ia1;
 +    gmx_bool              a_home[3];
 +    int                   nlocal;
 +    gmx_bool              bAssign;
 +
 +    ga2la  = dd->ga2la;
 +
 +    alook = gmx_mtop_atomlookup_settle_init(mtop);
 +
 +    nral = NRAL(F_SETTLE);
 +
 +    for (cg = cg_start; cg < cg_end; cg++)
 +    {
 +        if (GET_CGINFO_SETTLE(cginfo[cg]))
 +        {
 +            for (a = dd->cgindex[cg]; a < dd->cgindex[cg+1]; a++)
 +            {
 +                a_gl = dd->gatindex[a];
 +
 +                gmx_mtop_atomnr_to_molblock_ind(alook, a_gl, &mb, &molnr, &a_mol);
 +                molb = &mtop->molblock[mb];
 +
 +                settle = at2settle_mt[molb->type][a_mol];
 +
 +                if (settle >= 0)
 +                {
 +                    offset = a_gl - a_mol;
 +
 +                    ia1 = mtop->moltype[molb->type].ilist[F_SETTLE].iatoms;
 +
 +                    bAssign = FALSE;
 +                    nlocal  = 0;
 +                    for (sa = 0; sa < nral; sa++)
 +                    {
 +                        a_glsa     = offset + ia1[settle*(1+nral)+1+sa];
 +                        a_gls[sa]  = a_glsa;
 +                        a_home[sa] = ga2la_get_home(ga2la, a_glsa, &a_locs[sa]);
 +                        if (a_home[sa])
 +                        {
 +                            if (nlocal == 0 && a_gl == a_glsa)
 +                            {
 +                                bAssign = TRUE;
 +                            }
 +                            nlocal++;
 +                        }
 +                    }
 +
 +                    if (bAssign)
 +                    {
 +                        if (ils_local->nr+1+nral > ils_local->nalloc)
 +                        {
 +                            ils_local->nalloc = over_alloc_dd(ils_local->nr+1+nral);
 +                            srenew(ils_local->iatoms, ils_local->nalloc);
 +                        }
 +
 +                        ils_local->iatoms[ils_local->nr++] = ia1[settle*4];
 +
 +                        for (sa = 0; sa < nral; sa++)
 +                        {
 +                            if (ga2la_get_home(ga2la, a_gls[sa], &a_locs[sa]))
 +                            {
 +                                ils_local->iatoms[ils_local->nr++] = a_locs[sa];
 +                            }
 +                            else
 +                            {
 +                                ils_local->iatoms[ils_local->nr++] = -a_gls[sa] - 1;
 +                                /* Add this non-home atom to the list */
 +                                if (ireq->n+1 > ireq->nalloc)
 +                                {
 +                                    ireq->nalloc = over_alloc_large(ireq->n+1);
 +                                    srenew(ireq->ind, ireq->nalloc);
 +                                }
 +                                ireq->ind[ireq->n++] = a_gls[sa];
 +                                /* A check on double atom requests is
 +                                 * not required for settle.
 +                                 */
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +}
 +
 +static void atoms_to_constraints(gmx_domdec_t *dd,
 +                                 const gmx_mtop_t *mtop,
 +                                 const int *cginfo,
 +                                 const t_blocka *at2con_mt, int nrec,
 +                                 t_ilist *ilc_local,
 +                                 ind_req_t *ireq)
 +{
 +    const t_blocka           *at2con;
 +    gmx_ga2la_t               ga2la;
 +    gmx_mtop_atomlookup_t     alook;
 +    int                       ncon1;
 +    gmx_molblock_t           *molb;
 +    t_iatom                  *ia1, *ia2, *iap;
 +    int                       nhome, cg, a, a_gl, a_mol, a_loc, b_lo, offset, mb, molnr, b_mol, i, con, con_offset;
 +    gmx_domdec_constraints_t *dc;
 +    gmx_domdec_specat_comm_t *dcc;
 +
 +    dc  = dd->constraints;
 +    dcc = dd->constraint_comm;
 +
 +    ga2la  = dd->ga2la;
 +
 +    alook = gmx_mtop_atomlookup_init(mtop);
 +
 +    nhome = 0;
 +    for (cg = 0; cg < dd->ncg_home; cg++)
 +    {
 +        if (GET_CGINFO_CONSTR(cginfo[cg]))
 +        {
 +            for (a = dd->cgindex[cg]; a < dd->cgindex[cg+1]; a++)
 +            {
 +                a_gl = dd->gatindex[a];
 +
 +                gmx_mtop_atomnr_to_molblock_ind(alook, a_gl, &mb, &molnr, &a_mol);
 +                molb = &mtop->molblock[mb];
 +
 +                ncon1 = mtop->moltype[molb->type].ilist[F_CONSTR].nr/NRAL(F_SETTLE);
 +
 +                ia1 = mtop->moltype[molb->type].ilist[F_CONSTR].iatoms;
 +                ia2 = mtop->moltype[molb->type].ilist[F_CONSTRNC].iatoms;
 +
 +                /* Calculate the global constraint number offset for the molecule.
 +                 * This is only required for the global index to make sure
 +                 * that we use each constraint only once.
 +                 */
 +                con_offset =
 +                    dc->molb_con_offset[mb] + molnr*dc->molb_ncon_mol[mb];
 +
 +                /* The global atom number offset for this molecule */
 +                offset = a_gl - a_mol;
 +                at2con = &at2con_mt[molb->type];
 +                for (i = at2con->index[a_mol]; i < at2con->index[a_mol+1]; i++)
 +                {
 +                    con = at2con->a[i];
 +                    iap = constr_iatomptr(ncon1, ia1, ia2, con);
 +                    if (a_mol == iap[1])
 +                    {
 +                        b_mol = iap[2];
 +                    }
 +                    else
 +                    {
 +                        b_mol = iap[1];
 +                    }
 +                    if (ga2la_get_home(ga2la, offset+b_mol, &a_loc))
 +                    {
 +                        /* Add this fully home constraint at the first atom */
 +                        if (a_mol < b_mol)
 +                        {
 +                            if (dc->ncon+1 > dc->con_nalloc)
 +                            {
 +                                dc->con_nalloc = over_alloc_large(dc->ncon+1);
 +                                srenew(dc->con_gl, dc->con_nalloc);
 +                                srenew(dc->con_nlocat, dc->con_nalloc);
 +                            }
 +                            dc->con_gl[dc->ncon]     = con_offset + con;
 +                            dc->con_nlocat[dc->ncon] = 2;
 +                            if (ilc_local->nr + 3 > ilc_local->nalloc)
 +                            {
 +                                ilc_local->nalloc = over_alloc_dd(ilc_local->nr + 3);
 +                                srenew(ilc_local->iatoms, ilc_local->nalloc);
 +                            }
 +                            b_lo = a_loc;
 +                            ilc_local->iatoms[ilc_local->nr++] = iap[0];
 +                            ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? a    : b_lo);
 +                            ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? b_lo : a   );
 +                            dc->ncon++;
 +                            nhome++;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        /* We need the nrec constraints coupled to this constraint,
 +                         * so we need to walk out of the home cell by nrec+1 atoms,
 +                         * since already atom bg is not locally present.
 +                         * Therefore we call walk_out with nrec recursions to go
 +                         * after this first call.
 +                         */
 +                        walk_out(con, con_offset, b_mol, offset, nrec,
 +                                 ncon1, ia1, ia2, at2con,
 +                                 dd->ga2la, TRUE, dc, dcc, ilc_local, ireq);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Constraints: home %3d border %3d atoms: %3d\n",
 +                nhome, dc->ncon-nhome,
 +                dd->constraint_comm ? ireq->n : 0);
 +    }
 +}
 +
 +int dd_make_local_constraints(gmx_domdec_t *dd, int at_start,
 +                              const gmx_mtop_t *mtop,
 +                              const int *cginfo,
 +                              gmx_constr_t constr, int nrec,
 +                              t_ilist *il_local)
 +{
 +    gmx_domdec_constraints_t *dc;
 +    t_ilist                  *ilc_local, *ils_local;
 +    ind_req_t                *ireq;
 +    const t_blocka           *at2con_mt;
 +    const int               **at2settle_mt;
 +    gmx_hash_t                ga2la_specat;
 +    int at_end, i, j;
 +    t_iatom                  *iap;
 +
 +    dc = dd->constraints;
 +
 +    ilc_local = &il_local[F_CONSTR];
 +    ils_local = &il_local[F_SETTLE];
 +
 +    dc->ncon      = 0;
 +    ilc_local->nr = 0;
 +    if (dd->constraint_comm)
 +    {
 +        at2con_mt = atom2constraints_moltype(constr);
 +        ireq      = &dd->constraint_comm->ireq[0];
 +        ireq->n   = 0;
 +    }
 +    else
 +    {
 +        at2con_mt = NULL;
 +        ireq      = NULL;
 +    }
 +
 +    if (dd->bInterCGsettles)
 +    {
 +        at2settle_mt  = atom2settle_moltype(constr);
 +        ils_local->nr = 0;
 +    }
 +    else
 +    {
 +        /* Settle works inside charge groups, we assigned them already */
 +        at2settle_mt = NULL;
 +    }
 +
 +    if (at2settle_mt == NULL)
 +    {
 +        atoms_to_constraints(dd, mtop, cginfo, at2con_mt, nrec,
 +                             ilc_local, ireq);
 +    }
 +    else
 +    {
 +        int t0_set;
 +        int thread;
 +
 +        /* Do the constraints, if present, on the first thread.
 +         * Do the settles on all other threads.
 +         */
 +        t0_set = ((at2con_mt != NULL && dc->nthread > 1) ? 1 : 0);
 +
 +#pragma omp parallel for num_threads(dc->nthread) schedule(static)
 +        for (thread = 0; thread < dc->nthread; thread++)
 +        {
 +            if (at2con_mt && thread == 0)
 +            {
 +                atoms_to_constraints(dd, mtop, cginfo, at2con_mt, nrec,
 +                                     ilc_local, ireq);
 +            }
 +
 +            if (thread >= t0_set)
 +            {
 +                int        cg0, cg1;
 +                t_ilist   *ilst;
 +                ind_req_t *ireqt;
 +
 +                /* Distribute the settle check+assignments over
 +                 * dc->nthread or dc->nthread-1 threads.
 +                 */
 +                cg0 = (dd->ncg_home*(thread-t0_set  ))/(dc->nthread-t0_set);
 +                cg1 = (dd->ncg_home*(thread-t0_set+1))/(dc->nthread-t0_set);
 +
 +                if (thread == t0_set)
 +                {
 +                    ilst = ils_local;
 +                }
 +                else
 +                {
 +                    ilst = &dc->ils[thread];
 +                }
 +                ilst->nr = 0;
 +
 +                ireqt = &dd->constraint_comm->ireq[thread];
 +                if (thread > 0)
 +                {
 +                    ireqt->n = 0;
 +                }
 +
 +                atoms_to_settles(dd, mtop, cginfo, at2settle_mt,
 +                                 cg0, cg1,
 +                                 ilst, ireqt);
 +            }
 +        }
 +
 +        /* Combine the generate settles and requested indices */
 +        for (thread = 1; thread < dc->nthread; thread++)
 +        {
 +            t_ilist   *ilst;
 +            ind_req_t *ireqt;
 +            int        ia;
 +
 +            if (thread > t0_set)
 +            {
 +                ilst = &dc->ils[thread];
 +                if (ils_local->nr + ilst->nr > ils_local->nalloc)
 +                {
 +                    ils_local->nalloc = over_alloc_large(ils_local->nr + ilst->nr);
 +                    srenew(ils_local->iatoms, ils_local->nalloc);
 +                }
 +                for (ia = 0; ia < ilst->nr; ia++)
 +                {
 +                    ils_local->iatoms[ils_local->nr+ia] = ilst->iatoms[ia];
 +                }
 +                ils_local->nr += ilst->nr;
 +            }
 +
 +            ireqt = &dd->constraint_comm->ireq[thread];
 +            if (ireq->n+ireqt->n > ireq->nalloc)
 +            {
 +                ireq->nalloc = over_alloc_large(ireq->n+ireqt->n);
 +                srenew(ireq->ind, ireq->nalloc);
 +            }
 +            for (ia = 0; ia < ireqt->n; ia++)
 +            {
 +                ireq->ind[ireq->n+ia] = ireqt->ind[ia];
 +            }
 +            ireq->n += ireqt->n;
 +        }
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "Settles: total %3d\n", ils_local->nr/4);
 +        }
 +    }
 +
 +    if (dd->constraint_comm)
 +    {
 +        int nral1;
 +
 +        at_end =
 +            setup_specat_communication(dd, ireq, dd->constraint_comm,
 +                                       dd->constraints->ga2la,
 +                                       at_start, 2,
 +                                       "constraint", " or lincs-order");
 +
 +        /* Fill in the missing indices */
 +        ga2la_specat = dd->constraints->ga2la;
 +
 +        nral1 = 1 + NRAL(F_CONSTR);
 +        for (i = 0; i < ilc_local->nr; i += nral1)
 +        {
 +            iap = ilc_local->iatoms + i;
 +            for (j = 1; j < nral1; j++)
 +            {
 +                if (iap[j] < 0)
 +                {
 +                    iap[j] = gmx_hash_get_minone(ga2la_specat, -iap[j]-1);
 +                }
 +            }
 +        }
 +
 +        nral1 = 1 + NRAL(F_SETTLE);
 +        for (i = 0; i < ils_local->nr; i += nral1)
 +        {
 +            iap = ils_local->iatoms + i;
 +            for (j = 1; j < nral1; j++)
 +            {
 +                if (iap[j] < 0)
 +                {
 +                    iap[j] = gmx_hash_get_minone(ga2la_specat, -iap[j]-1);
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        at_end = at_start;
 +    }
 +
 +    return at_end;
 +}
 +
 +int dd_make_local_vsites(gmx_domdec_t *dd, int at_start, t_ilist *lil)
 +{
 +    gmx_domdec_specat_comm_t *spac;
 +    ind_req_t                *ireq;
 +    gmx_hash_t                ga2la_specat;
 +    int  ftype, nral, i, j, gat, a;
 +    t_ilist                  *lilf;
 +    t_iatom                  *iatoms;
 +    int  at_end;
 +
 +    spac         = dd->vsite_comm;
 +    ireq         = &spac->ireq[0];
 +    ga2la_specat = dd->ga2la_vsite;
 +
 +    ireq->n = 0;
 +    /* Loop over all the home vsites */
 +    for (ftype = 0; ftype < F_NRE; ftype++)
 +    {
 +        if (interaction_function[ftype].flags & IF_VSITE)
 +        {
 +            nral = NRAL(ftype);
 +            lilf = &lil[ftype];
 +            for (i = 0; i < lilf->nr; i += 1+nral)
 +            {
 +                iatoms = lilf->iatoms + i;
 +                /* Check if we have the other atoms */
 +                for (j = 1; j < 1+nral; j++)
 +                {
 +                    if (iatoms[j] < 0)
 +                    {
 +                        /* This is not a home atom,
 +                         * we need to ask our neighbors.
 +                         */
 +                        a = -iatoms[j] - 1;
 +                        /* Check to not ask for the same atom more than once */
 +                        if (gmx_hash_get_minone(dd->ga2la_vsite, a) == -1)
 +                        {
 +                            /* Add this non-home atom to the list */
 +                            if (ireq->n+1 > ireq->nalloc)
 +                            {
 +                                ireq->nalloc = over_alloc_large(ireq->n+1);
 +                                srenew(ireq->ind, ireq->nalloc);
 +                            }
 +                            ireq->ind[ireq->n++] = a;
 +                            /* Temporarily mark with -2,
 +                             * we get the index later.
 +                             */
 +                            gmx_hash_set(ga2la_specat, a, -2);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    at_end = setup_specat_communication(dd, ireq, dd->vsite_comm, ga2la_specat,
 +                                        at_start, 1, "vsite", "");
 +
 +    /* Fill in the missing indices */
 +    for (ftype = 0; ftype < F_NRE; ftype++)
 +    {
 +        if (interaction_function[ftype].flags & IF_VSITE)
 +        {
 +            nral = NRAL(ftype);
 +            lilf = &lil[ftype];
 +            for (i = 0; i < lilf->nr; i += 1+nral)
 +            {
 +                iatoms = lilf->iatoms + i;
 +                for (j = 1; j < 1+nral; j++)
 +                {
 +                    if (iatoms[j] < 0)
 +                    {
 +                        iatoms[j] = gmx_hash_get_minone(ga2la_specat, -iatoms[j]-1);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return at_end;
 +}
 +
 +static gmx_domdec_specat_comm_t *specat_comm_init(int nthread)
 +{
 +    gmx_domdec_specat_comm_t *spac;
 +
 +    snew(spac, 1);
 +    spac->nthread = nthread;
 +    snew(spac->ireq, spac->nthread);
 +
 +    return spac;
 +}
 +
 +void init_domdec_constraints(gmx_domdec_t *dd,
 +                             gmx_mtop_t   *mtop)
 +{
 +    gmx_domdec_constraints_t *dc;
 +    gmx_molblock_t           *molb;
 +    int mb, ncon, c, a;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Begin init_domdec_constraints\n");
 +    }
 +
 +    snew(dd->constraints, 1);
 +    dc = dd->constraints;
 +
 +    snew(dc->molb_con_offset, mtop->nmolblock);
 +    snew(dc->molb_ncon_mol, mtop->nmolblock);
 +
 +    ncon = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb                    = &mtop->molblock[mb];
 +        dc->molb_con_offset[mb] = ncon;
 +        dc->molb_ncon_mol[mb]   =
 +            mtop->moltype[molb->type].ilist[F_CONSTR].nr/3 +
 +            mtop->moltype[molb->type].ilist[F_CONSTRNC].nr/3;
 +        ncon += molb->nmol*dc->molb_ncon_mol[mb];
 +    }
 +
 +    if (ncon > 0)
 +    {
 +        snew(dc->gc_req, ncon);
 +        for (c = 0; c < ncon; c++)
 +        {
 +            dc->gc_req[c] = 0;
 +        }
 +    }
 +
 +    /* Use a hash table for the global to local index.
 +     * The number of keys is a rough estimate, it will be optimized later.
 +     */
 +    dc->ga2la = gmx_hash_init(min(mtop->natoms/20,
 +                                  mtop->natoms/(2*dd->nnodes)));
 +
 +    dc->nthread = gmx_omp_nthreads_get(emntDomdec);
 +    snew(dc->ils, dc->nthread);
 +
 +    dd->constraint_comm = specat_comm_init(dc->nthread);
 +}
 +
 +void init_domdec_vsites(gmx_domdec_t *dd, int n_intercg_vsite)
 +{
 +    int i;
 +    gmx_domdec_constraints_t *dc;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Begin init_domdec_vsites\n");
 +    }
 +
 +    /* Use a hash table for the global to local index.
 +     * The number of keys is a rough estimate, it will be optimized later.
 +     */
 +    dd->ga2la_vsite = gmx_hash_init(min(n_intercg_vsite/20,
 +                                        n_intercg_vsite/(2*dd->nnodes)));
 +
 +    dd->vsite_comm = specat_comm_init(1);
 +}