Merge release-4-6 into release-5-0
authorRoland Schulz <roland@utk.edu>
Mon, 26 May 2014 08:49:25 +0000 (04:49 -0400)
committerMark Abraham <mark.j.abraham@gmail.com>
Tue, 27 May 2014 07:50:27 +0000 (09:50 +0200)
Conflicts:
CMakeLists.txt - fixed use of GMX_CPU_ACCELERATION
src/gromacs/gmxana/gmx_lie.c
src/gromacs/pulling/pull.c

Change-Id: I45a70eee6f7ec4dd922b6c04c740c40f50159a45

1  2 
CMakeLists.txt
src/gromacs/gmxana/gmx_covar.c
src/gromacs/gmxana/gmx_lie.c
src/gromacs/gmxpreprocess/addconf.c
src/gromacs/gmxpreprocess/grompp.c
src/gromacs/gmxpreprocess/readir.c
src/gromacs/mdlib/forcerec.c
src/gromacs/mdlib/md_support.c
src/gromacs/pulling/pull.c
src/programs/mdrun/md.c

diff --cc CMakeLists.txt
index 03f24b6f0ca6e056031a86d5cb99dc4e9dff8f43,e69ee1ef7252419f847b6de7b8eb5724caaca814..4f1f62b6dd27e5f3becd3a12f3f3bbf730691fc0
@@@ -316,14 -281,56 +316,26 @@@ include(gmxCFlags
  gmx_c_flags()
  
  # This variable should be used for additional compiler flags which are not
 -# generated in gmxCFlags nor are acceleration or MPI related.
 +# generated in gmxCFlags nor are SIMD or MPI related.
  set(EXTRA_C_FLAGS "")
 +set(EXTRA_CXX_FLAGS "")
  
 -# gcc 4.4.x is buggy and crashes when compiling some files with O3 and OpenMP on.
 -# Detect here whether applying a workaround is needed and will apply it later
 -# on the affected files.
 -include(gmxGCC44O3BugWorkaround)
 -gmx_check_gcc44_bug_workaround_needed(GMX_USE_GCC44_BUG_WORKAROUND)
 -
 -# clang 3.0 is buggy for some unknown reason detected during adding
 -# the SSE2 group kernels for GROMACS 4.6. If we ever work out what
 -# that is, we should replace these tests with a compiler feature test,
 -# update GROMACS Redmine task #1039 and perhaps report a clang bug.
 -#
 -# In the meantime, until we require CMake 2.8.10 we cannot rely on it to detect
 -# the compiler version for us. So we need a manual check for clang 3.0.
 -include(gmxDetectClang30)
 -gmx_detect_clang_3_0(COMPILER_IS_CLANG_3_0)
 -if(COMPILER_IS_CLANG_3_0)
 -    message(FATAL_ERROR "Your compiler is clang version 3.0, which is known to be buggy for GROMACS. Use a different compiler.")
 -endif()
 -
 -# clang <=3.2 contains a bug that causes incorrect code to be generated for the
 -# vfmaddps instruction and therefore the bug is triggered with AVX_128_FMA.
 -# (see: http://llvm.org/bugs/show_bug.cgi?id=15040).
 -# We can work around this by not using the integrated assembler (except on OS X
 -# which has an outdated assembler that does not support AVX instructions).
 -if (${CMAKE_C_COMPILER_ID} MATCHES "Clang" AND C_COMPILER_VERSION VERSION_LESS "3.3")
 -    set(GMX_USE_CLANG_FMA_BUG_WORKAROUND TRUE)
 -endif()
 -
 -# GMX_CPU_ACCELERATION will not be set automatically until the second
 +# Run through a number of tests for buggy compilers and other issues
 +include(gmxTestCompilerProblems)
 +gmx_test_compiler_problems()
++# GMX_SIMD will not be set automatically until the second
+ # pass (which is not strictly guaranteed to occur), so putting this
+ # check here among logically-related tests is inefficient, but the
+ # potential loss is likely zero.
 -if(GMX_CPU_ACCELERATION STREQUAL "AVX_256"
++if(GMX_SIMD STREQUAL "AVX_256"
+         AND CMAKE_COMPILER_IS_GNUCC
+         AND (C_COMPILER_VERSION VERSION_EQUAL "4.6.1"
+             OR CXX_COMPILER_VERSION VERSION_EQUAL "4.6.1"))
 -    message(FATAL_ERROR "gcc 4.6.1 has buggy support for AVX, and GROMACS mdrun will not work. If you want simulation performance, use a more recent compiler. Otherwise, use GMX_CPU_ACCELERATION=SSE4.1")
++    message(FATAL_ERROR "gcc 4.6.1 has buggy support for AVX, and GROMACS mdrun will not work. If you want simulation performance, use a more recent compiler. Otherwise, use GMX_SIMD=SSE4.1")
+     # See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49002
+ endif()
 -if (CMAKE_C_COMPILER_ID STREQUAL "PGI")
 -    message(WARNING "All tested PGI compiler versions (up to 12.9.0) generate binaries which produce incorrect results, or even fail to compile Gromacs. Highly recommended to use a different compiler. If you choose to use PGI, make sure to run the regressiontests.")
 -endif()
  
 -if(CMAKE_C_COMPILER_ID MATCHES "Intel" AND C_COMPILER_VERSION VERSION_LESS "12.0.0")
 -    message(WARNING "Intel compilers before 12.0.0 are not routinely tested, so there may be problems. Version 11.1 with SSE4.1 is known to produce incorrect results. It is highly recommended to use a more up-to-date compiler. If you choose to use this version, make sure you run the regressiontests.")
 -endif()
  
  set(PKG_CFLAGS "")
  if(GMX_DOUBLE)
index 501597e9acb7addff70ca18cbb79a2a8eb22395d,0000000000000000000000000000000000000000..c9faa784de876a09870107c1decc63a88341790f
mode 100644,000000..100644
--- /dev/null
@@@ -1,657 -1,0 +1,660 @@@
-     for (i = 0; (i < ndim); i++)
 +/*
 + * 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 <math.h>
 +#include <string.h>
 +#include <time.h>
 +
 +#ifdef HAVE_SYS_TIME_H
 +#include <sys/time.h>
 +#endif
 +
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "mshift.h"
 +#include "xvgr.h"
 +#include "rmpbc.h"
 +#include "txtdump.h"
 +#include "gromacs/fileio/matio.h"
 +#include "eigio.h"
 +#include "physics.h"
 +#include "gmx_ana.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +#include "gromacs/linearalgebra/eigensolver.h"
 +#include "gromacs/math/do_fit.h"
 +#include "gromacs/legacyheaders/gmx_fatal.h"
 +
 +int gmx_covar(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] calculates and diagonalizes the (mass-weighted)",
 +        "covariance matrix.",
 +        "All structures are fitted to the structure in the structure file.",
 +        "When this is not a run input file periodicity will not be taken into",
 +        "account. When the fit and analysis groups are identical and the analysis",
 +        "is non mass-weighted, the fit will also be non mass-weighted.",
 +        "[PAR]",
 +        "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
 +        "When the same atoms are used for the fit and the covariance analysis,",
 +        "the reference structure for the fit is written first with t=-1.",
 +        "The average (or reference when [TT]-ref[tt] is used) structure is",
 +        "written with t=0, the eigenvectors",
 +        "are written as frames with the eigenvector number as timestamp.",
 +        "[PAR]",
 +        "The eigenvectors can be analyzed with [gmx-anaeig].",
 +        "[PAR]",
 +        "Option [TT]-ascii[tt] writes the whole covariance matrix to",
 +        "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
 +        "[PAR]",
 +        "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
 +        "[PAR]",
 +        "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
 +        "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
 +        "written.",
 +        "[PAR]",
 +        "Note that the diagonalization of a matrix requires memory and time",
 +        "that will increase at least as fast as than the square of the number",
 +        "of atoms involved. It is easy to run out of memory, in which",
 +        "case this tool will probably exit with a 'Segmentation fault'. You",
 +        "should consider carefully whether a reduced set of atoms will meet",
 +        "your needs for lower costs."
 +    };
 +    static gmx_bool bFit = TRUE, bRef = FALSE, bM = FALSE, bPBC = TRUE;
 +    static int      end  = -1;
 +    t_pargs         pa[] = {
 +        { "-fit",  FALSE, etBOOL, {&bFit},
 +          "Fit to a reference structure"},
 +        { "-ref",  FALSE, etBOOL, {&bRef},
 +          "Use the deviation from the conformation in the structure file instead of from the average" },
 +        { "-mwa",  FALSE, etBOOL, {&bM},
 +          "Mass-weighted covariance analysis"},
 +        { "-last",  FALSE, etINT, {&end},
 +          "Last eigenvector to write away (-1 is till the last)" },
 +        { "-pbc",  FALSE,  etBOOL, {&bPBC},
 +          "Apply corrections for periodic boundary conditions" }
 +    };
 +    FILE           *out;
 +    t_trxstatus    *status;
 +    t_trxstatus    *trjout;
 +    t_topology      top;
 +    int             ePBC;
 +    t_atoms        *atoms;
 +    rvec           *x, *xread, *xref, *xav, *xproj;
 +    matrix          box, zerobox;
 +    real           *sqrtm, *mat, *eigenvalues, sum, trace, inv_nframes;
 +    real            t, tstart, tend, **mat2;
 +    real            xj, *w_rls = NULL;
 +    real            min, max, *axis;
 +    int             ntopatoms, step;
 +    int             natoms, nat, count, nframes0, nframes, nlevels;
 +    gmx_int64_t     ndim, i, j, k, l;
 +    int             WriteXref;
 +    const char     *fitfile, *trxfile, *ndxfile;
 +    const char     *eigvalfile, *eigvecfile, *averfile, *logfile;
 +    const char     *asciifile, *xpmfile, *xpmafile;
 +    char            str[STRLEN], *fitname, *ananame, *pcwd;
 +    int             d, dj, nfit;
 +    atom_id        *index, *ifit;
 +    gmx_bool        bDiffMass1, bDiffMass2;
 +    time_t          now;
 +    char            timebuf[STRLEN];
 +    t_rgb           rlo, rmi, rhi;
 +    real           *eigenvectors;
 +    output_env_t    oenv;
 +    gmx_rmpbc_t     gpbc = NULL;
 +
 +    t_filenm        fnm[] = {
 +        { efTRX, "-f",  NULL, ffREAD },
 +        { efTPS, NULL,  NULL, ffREAD },
 +        { efNDX, NULL,  NULL, ffOPTRD },
 +        { efXVG, NULL,  "eigenval", ffWRITE },
 +        { efTRN, "-v",  "eigenvec", ffWRITE },
 +        { efSTO, "-av", "average.pdb", ffWRITE },
 +        { efLOG, NULL,  "covar", ffWRITE },
 +        { efDAT, "-ascii", "covar", ffOPTWR },
 +        { efXPM, "-xpm", "covar", ffOPTWR },
 +        { efXPM, "-xpma", "covara", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    clear_mat(zerobox);
 +
 +    fitfile    = ftp2fn(efTPS, NFILE, fnm);
 +    trxfile    = ftp2fn(efTRX, NFILE, fnm);
 +    ndxfile    = ftp2fn_null(efNDX, NFILE, fnm);
 +    eigvalfile = ftp2fn(efXVG, NFILE, fnm);
 +    eigvecfile = ftp2fn(efTRN, NFILE, fnm);
 +    averfile   = ftp2fn(efSTO, NFILE, fnm);
 +    logfile    = ftp2fn(efLOG, NFILE, fnm);
 +    asciifile  = opt2fn_null("-ascii", NFILE, fnm);
 +    xpmfile    = opt2fn_null("-xpm", NFILE, fnm);
 +    xpmafile   = opt2fn_null("-xpma", NFILE, fnm);
 +
 +    read_tps_conf(fitfile, str, &top, &ePBC, &xref, NULL, box, TRUE);
 +    atoms = &top.atoms;
 +
 +    if (bFit)
 +    {
 +        printf("\nChoose a group for the least squares fit\n");
 +        get_index(atoms, ndxfile, 1, &nfit, &ifit, &fitname);
 +        if (nfit < 3)
 +        {
 +            gmx_fatal(FARGS, "Need >= 3 points to fit!\n");
 +        }
 +    }
 +    else
 +    {
 +        nfit = 0;
 +    }
 +    printf("\nChoose a group for the covariance analysis\n");
 +    get_index(atoms, ndxfile, 1, &natoms, &index, &ananame);
 +
 +    bDiffMass1 = FALSE;
 +    if (bFit)
 +    {
 +        snew(w_rls, atoms->nr);
 +        for (i = 0; (i < nfit); i++)
 +        {
 +            w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
 +            if (i)
 +            {
 +                bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]] != w_rls[ifit[i-1]]);
 +            }
 +        }
 +    }
 +    bDiffMass2 = FALSE;
 +    snew(sqrtm, natoms);
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        if (bM)
 +        {
 +            sqrtm[i] = sqrt(atoms->atom[index[i]].m);
 +            if (i)
 +            {
 +                bDiffMass2 = bDiffMass2 || (sqrtm[i] != sqrtm[i-1]);
 +            }
 +        }
 +        else
 +        {
 +            sqrtm[i] = 1.0;
 +        }
 +    }
 +
 +    if (bFit && bDiffMass1 && !bDiffMass2)
 +    {
 +        bDiffMass1 = natoms != nfit;
 +        i          = 0;
 +        for (i = 0; (i < natoms) && !bDiffMass1; i++)
 +        {
 +            bDiffMass1 = index[i] != ifit[i];
 +        }
 +        if (!bDiffMass1)
 +        {
 +            fprintf(stderr, "\n"
 +                    "Note: the fit and analysis group are identical,\n"
 +                    "      while the fit is mass weighted and the analysis is not.\n"
 +                    "      Making the fit non mass weighted.\n\n");
 +            for (i = 0; (i < nfit); i++)
 +            {
 +                w_rls[ifit[i]] = 1.0;
 +            }
 +        }
 +    }
 +
 +    /* Prepare reference frame */
 +    if (bPBC)
 +    {
 +        gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
 +        gmx_rmpbc(gpbc, atoms->nr, box, xref);
 +    }
 +    if (bFit)
 +    {
 +        reset_x(nfit, ifit, atoms->nr, NULL, xref, w_rls);
 +    }
 +
 +    snew(x, natoms);
 +    snew(xav, natoms);
 +    ndim = natoms*DIM;
 +    if (sqrt(GMX_INT64_MAX) < ndim)
 +    {
 +        gmx_fatal(FARGS, "Number of degrees of freedoms to large for matrix.\n");
 +    }
 +    snew(mat, ndim*ndim);
 +
 +    fprintf(stderr, "Calculating the average structure ...\n");
 +    nframes0 = 0;
 +    nat      = read_first_x(oenv, &status, trxfile, &t, &xread, box);
 +    if (nat != atoms->nr)
 +    {
 +        fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, nat);
 +    }
 +    do
 +    {
 +        nframes0++;
 +        /* calculate x: a fitted struture of the selected atoms */
 +        if (bPBC)
 +        {
 +            gmx_rmpbc(gpbc, nat, box, xread);
 +        }
 +        if (bFit)
 +        {
 +            reset_x(nfit, ifit, nat, NULL, xread, w_rls);
 +            do_fit(nat, w_rls, xref, xread);
 +        }
 +        for (i = 0; i < natoms; i++)
 +        {
 +            rvec_inc(xav[i], xread[index[i]]);
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, xread, box));
 +    close_trj(status);
 +
 +    inv_nframes = 1.0/nframes0;
 +    for (i = 0; i < natoms; i++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            xav[i][d]         *= inv_nframes;
 +            xread[index[i]][d] = xav[i][d];
 +        }
 +    }
 +    write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure",
 +                           atoms, xread, NULL, epbcNONE, zerobox, natoms, index);
 +    sfree(xread);
 +
 +    fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", (int)ndim, (int)ndim);
 +    nframes = 0;
 +    nat     = read_first_x(oenv, &status, trxfile, &t, &xread, box);
 +    tstart  = t;
 +    do
 +    {
 +        nframes++;
 +        tend = t;
 +        /* calculate x: a (fitted) structure of the selected atoms */
 +        if (bPBC)
 +        {
 +            gmx_rmpbc(gpbc, nat, box, xread);
 +        }
 +        if (bFit)
 +        {
 +            reset_x(nfit, ifit, nat, NULL, xread, w_rls);
 +            do_fit(nat, w_rls, xref, xread);
 +        }
 +        if (bRef)
 +        {
 +            for (i = 0; i < natoms; i++)
 +            {
 +                rvec_sub(xread[index[i]], xref[index[i]], x[i]);
 +            }
 +        }
 +        else
 +        {
 +            for (i = 0; i < natoms; i++)
 +            {
 +                rvec_sub(xread[index[i]], xav[i], x[i]);
 +            }
 +        }
 +
 +        for (j = 0; j < natoms; j++)
 +        {
 +            for (dj = 0; dj < DIM; dj++)
 +            {
 +                k  = ndim*(DIM*j+dj);
 +                xj = x[j][dj];
 +                for (i = j; i < natoms; i++)
 +                {
 +                    l = k+DIM*i;
 +                    for (d = 0; d < DIM; d++)
 +                    {
 +                        mat[l+d] += x[i][d]*xj;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, xread, box) &&
 +           (bRef || nframes < nframes0));
 +    close_trj(status);
 +    gmx_rmpbc_done(gpbc);
 +
 +    fprintf(stderr, "Read %d frames\n", nframes);
 +
 +    if (bRef)
 +    {
 +        /* copy the reference structure to the ouput array x */
 +        snew(xproj, natoms);
 +        for (i = 0; i < natoms; i++)
 +        {
 +            copy_rvec(xref[index[i]], xproj[i]);
 +        }
 +    }
 +    else
 +    {
 +        xproj = xav;
 +    }
 +
 +    /* correct the covariance matrix for the mass */
 +    inv_nframes = 1.0/nframes;
 +    for (j = 0; j < natoms; j++)
 +    {
 +        for (dj = 0; dj < DIM; dj++)
 +        {
 +            for (i = j; i < natoms; i++)
 +            {
 +                k = ndim*(DIM*j+dj)+DIM*i;
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* symmetrize the matrix */
 +    for (j = 0; j < ndim; j++)
 +    {
 +        for (i = j; i < ndim; i++)
 +        {
 +            mat[ndim*i+j] = mat[ndim*j+i];
 +        }
 +    }
 +
 +    trace = 0;
 +    for (i = 0; i < ndim; i++)
 +    {
 +        trace += mat[i*ndim+i];
 +    }
 +    fprintf(stderr, "\nTrace of the covariance matrix: %g (%snm^2)\n",
 +            trace, bM ? "u " : "");
 +
 +    if (asciifile)
 +    {
 +        out = gmx_ffopen(asciifile, "w");
 +        for (j = 0; j < ndim; j++)
 +        {
 +            for (i = 0; i < ndim; i += 3)
 +            {
 +                fprintf(out, "%g %g %g\n",
 +                        mat[ndim*j+i], mat[ndim*j+i+1], mat[ndim*j+i+2]);
 +            }
 +        }
 +        gmx_ffclose(out);
 +    }
 +
 +    if (xpmfile)
 +    {
 +        min = 0;
 +        max = 0;
 +        snew(mat2, ndim);
 +        for (j = 0; j < ndim; j++)
 +        {
 +            mat2[j] = &(mat[ndim*j]);
 +            for (i = 0; i <= j; i++)
 +            {
 +                if (mat2[j][i] < min)
 +                {
 +                    min = mat2[j][i];
 +                }
 +                if (mat2[j][j] > max)
 +                {
 +                    max = mat2[j][i];
 +                }
 +            }
 +        }
 +        snew(axis, ndim);
 +        for (i = 0; i < ndim; i++)
 +        {
 +            axis[i] = i+1;
 +        }
 +        rlo.r   = 0; rlo.g = 0; rlo.b = 1;
 +        rmi.r   = 1; rmi.g = 1; rmi.b = 1;
 +        rhi.r   = 1; rhi.g = 0; rhi.b = 0;
 +        out     = gmx_ffopen(xpmfile, "w");
 +        nlevels = 80;
 +        write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
 +                   "dim", "dim", ndim, ndim, axis, axis,
 +                   mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
 +        gmx_ffclose(out);
 +        sfree(axis);
 +        sfree(mat2);
 +    }
 +
 +    if (xpmafile)
 +    {
 +        min = 0;
 +        max = 0;
 +        snew(mat2, ndim/DIM);
 +        for (i = 0; i < ndim/DIM; i++)
 +        {
 +            snew(mat2[i], ndim/DIM);
 +        }
 +        for (j = 0; j < ndim/DIM; j++)
 +        {
 +            for (i = 0; i <= j; i++)
 +            {
 +                mat2[j][i] = 0;
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
 +                }
 +                if (mat2[j][i] < min)
 +                {
 +                    min = mat2[j][i];
 +                }
 +                if (mat2[j][j] > max)
 +                {
 +                    max = mat2[j][i];
 +                }
 +                mat2[i][j] = mat2[j][i];
 +            }
 +        }
 +        snew(axis, ndim/DIM);
 +        for (i = 0; i < ndim/DIM; i++)
 +        {
 +            axis[i] = i+1;
 +        }
 +        rlo.r   = 0; rlo.g = 0; rlo.b = 1;
 +        rmi.r   = 1; rmi.g = 1; rmi.b = 1;
 +        rhi.r   = 1; rhi.g = 0; rhi.b = 0;
 +        out     = gmx_ffopen(xpmafile, "w");
 +        nlevels = 80;
 +        write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
 +                   "atom", "atom", ndim/DIM, ndim/DIM, axis, axis,
 +                   mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
 +        gmx_ffclose(out);
 +        sfree(axis);
 +        for (i = 0; i < ndim/DIM; i++)
 +        {
 +            sfree(mat2[i]);
 +        }
 +        sfree(mat2);
 +    }
 +
 +
 +    /* call diagonalization routine */
 +
 +    snew(eigenvalues, ndim);
 +    snew(eigenvectors, ndim*ndim);
 +
 +    memcpy(eigenvectors, mat, ndim*ndim*sizeof(real));
 +    fprintf(stderr, "\nDiagonalizing ...\n");
 +    fflush(stderr);
 +    eigensolver(eigenvectors, ndim, 0, ndim, eigenvalues, mat);
 +    sfree(eigenvectors);
 +
 +    /* now write the output */
 +
 +    sum = 0;
 +    for (i = 0; i < ndim; i++)
 +    {
 +        sum += eigenvalues[i];
 +    }
 +    fprintf(stderr, "\nSum of the eigenvalues: %g (%snm^2)\n",
 +            sum, bM ? "u " : "");
 +    if (fabs(trace-sum) > 0.01*trace)
 +    {
 +        fprintf(stderr, "\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
 +    }
 +
 +    fprintf(stderr, "\nWriting eigenvalues to %s\n", eigvalfile);
 +
 +    sprintf(str, "(%snm\\S2\\N)", bM ? "u " : "");
 +    out = xvgropen(eigvalfile,
 +                   "Eigenvalues of the covariance matrix",
 +                   "Eigenvector index", str, oenv);
-     fprintf(out, "Wrote %d eigenvalues to %s\n", (int)ndim, eigvalfile);
++    for (i = 0; (i < end); i++)
 +    {
 +        fprintf (out, "%10d %g\n", (int)i+1, eigenvalues[ndim-1-i]);
 +    }
 +    gmx_ffclose(out);
 +
 +    if (end == -1)
 +    {
 +        if (nframes-1 < ndim)
 +        {
 +            end = nframes-1;
++            fprintf(out, "WARNING: there are fewer frames in your trajectory than there are\n");
++            fprintf(out, "degrees of freedom in your system. Only generating the first\n");
++            fprintf(out, "%d out of %d eigenvectors and eigenvalues.\n", end, (int)ndim);
 +        }
 +        else
 +        {
 +            end = ndim;
 +        }
 +    }
 +    if (bFit)
 +    {
 +        /* misuse lambda: 0/1 mass weighted analysis no/yes */
 +        if (nfit == natoms)
 +        {
 +            WriteXref = eWXR_YES;
 +            for (i = 0; i < nfit; i++)
 +            {
 +                copy_rvec(xref[ifit[i]], x[i]);
 +            }
 +        }
 +        else
 +        {
 +            WriteXref = eWXR_NO;
 +        }
 +    }
 +    else
 +    {
 +        /* misuse lambda: -1 for no fit */
 +        WriteXref = eWXR_NOFIT;
 +    }
 +
 +    write_eigenvectors(eigvecfile, natoms, mat, TRUE, 1, end,
 +                       WriteXref, x, bDiffMass1, xproj, bM, eigenvalues);
 +
 +    out = gmx_ffopen(logfile, "w");
 +
 +    time(&now);
 +    gmx_ctime_r(&now, timebuf, STRLEN);
 +    fprintf(out, "Covariance analysis log, written %s\n", timebuf);
 +
 +    fprintf(out, "Program: %s\n", argv[0]);
 +    gmx_getcwd(str, STRLEN);
 +
 +    fprintf(out, "Working directory: %s\n\n", str);
 +
 +    fprintf(out, "Read %d frames from %s (time %g to %g %s)\n", nframes, trxfile,
 +            output_env_conv_time(oenv, tstart), output_env_conv_time(oenv, tend), output_env_get_time_unit(oenv));
 +    if (bFit)
 +    {
 +        fprintf(out, "Read reference structure for fit from %s\n", fitfile);
 +    }
 +    if (ndxfile)
 +    {
 +        fprintf(out, "Read index groups from %s\n", ndxfile);
 +    }
 +    fprintf(out, "\n");
 +
 +    fprintf(out, "Analysis group is '%s' (%d atoms)\n", ananame, natoms);
 +    if (bFit)
 +    {
 +        fprintf(out, "Fit group is '%s' (%d atoms)\n", fitname, nfit);
 +    }
 +    else
 +    {
 +        fprintf(out, "No fit was used\n");
 +    }
 +    fprintf(out, "Analysis is %smass weighted\n", bDiffMass2 ? "" : "non-");
 +    if (bFit)
 +    {
 +        fprintf(out, "Fit is %smass weighted\n", bDiffMass1 ? "" : "non-");
 +    }
 +    fprintf(out, "Diagonalized the %dx%d covariance matrix\n", (int)ndim, (int)ndim);
 +    fprintf(out, "Trace of the covariance matrix before diagonalizing: %g\n",
 +            trace);
 +    fprintf(out, "Trace of the covariance matrix after diagonalizing: %g\n\n",
 +            sum);
 +
++    fprintf(out, "Wrote %d eigenvalues to %s\n", (int)end, eigvalfile);
 +    if (WriteXref == eWXR_YES)
 +    {
 +        fprintf(out, "Wrote reference structure to %s\n", eigvecfile);
 +    }
 +    fprintf(out, "Wrote average structure to %s and %s\n", averfile, eigvecfile);
 +    fprintf(out, "Wrote eigenvectors %d to %d to %s\n", 1, end, eigvecfile);
 +
 +    gmx_ffclose(out);
 +
 +    fprintf(stderr, "Wrote the log to %s\n", logfile);
 +
 +    return 0;
 +}
index 3e29541e9eeda3cb426f9ed9e196542b02827d22,0000000000000000000000000000000000000000..7a9a689cc833cbdc61704f12eba7ec0906dc57dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,218 -1,0 +1,224 @@@
-         "from. One needs an energy file with the following components:",
-         "Coul (A-B) LJ-SR (A-B) etc."
 +/*
 + * 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 <stdio.h>
 +#include <stdlib.h>
 +#include <math.h>
 +#include <string.h>
 +
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "gromacs/fileio/futil.h"
 +#include "txtdump.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "gstat.h"
 +#include "xvgr.h"
 +#include "gmx_ana.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +typedef struct {
 +    int  nlj, nqq;
 +    int *lj;
 +    int *qq;
 +} t_liedata;
 +
 +static t_liedata *analyze_names(int nre, gmx_enxnm_t *names, const char *ligand)
 +{
 +    int        i;
 +    t_liedata *ld;
 +    char       self[256];
 +
 +    /* Skip until we come to pressure */
 +    for (i = 0; (i < F_NRE); i++)
 +    {
 +        if (strcmp(names[i].name, interaction_function[F_PRES].longname) == 0)
 +        {
 +            break;
 +        }
 +    }
 +
 +    /* Now real analysis: find components of energies */
 +    sprintf(self, "%s-%s", ligand, ligand);
 +    snew(ld, 1);
 +    for (; (i < nre); i++)
 +    {
 +        if ((strstr(names[i].name, ligand) != NULL) &&
 +            (strstr(names[i].name, self) == NULL))
 +        {
 +            if (strstr(names[i].name, "LJ") != NULL)
 +            {
 +                ld->nlj++;
 +                srenew(ld->lj, ld->nlj);
 +                ld->lj[ld->nlj-1] = i;
 +            }
 +            else if (strstr(names[i].name, "Coul") != NULL)
 +            {
 +                ld->nqq++;
 +                srenew(ld->qq, ld->nqq);
 +                ld->qq[ld->nqq-1] = i;
 +            }
 +        }
 +    }
 +    printf("Using the following energy terms:\n");
 +    printf("LJ:  ");
 +    for (i = 0; (i < ld->nlj); i++)
 +    {
 +        printf("  %12s", names[ld->lj[i]].name);
 +    }
 +    printf("\nCoul:");
 +    for (i = 0; (i < ld->nqq); i++)
 +    {
 +        printf("  %12s", names[ld->qq[i]].name);
 +    }
 +    printf("\n");
 +
 +    return ld;
 +}
 +
 +real calc_lie(t_liedata *ld, t_energy ee[], real lie_lj, real lie_qq,
 +              real fac_lj, real fac_qq)
 +{
 +    int  i;
 +    real lj_tot, qq_tot;
 +
 +    lj_tot = 0;
 +    for (i = 0; (i < ld->nlj); i++)
 +    {
 +        lj_tot += ee[ld->lj[i]].e;
 +    }
 +    qq_tot = 0;
 +    for (i = 0; (i < ld->nqq); i++)
 +    {
 +        qq_tot += ee[ld->qq[i]].e;
 +    }
 +
 +    /* And now the great LIE formula: */
 +    return fac_lj*(lj_tot-lie_lj)+fac_qq*(qq_tot-lie_qq);
 +}
 +
 +int gmx_lie(int argc, char *argv[])
 +{
 +    const char        *desc[] = {
 +        "[THISMODULE] computes a free energy estimate based on an energy analysis",
++        "from nonbonded energies. One needs an energy file with the following components:",
++        "Coul-(A-B) LJ-SR (A-B) etc.[PAR]",
++        "To utilize [TT]g_lie[tt] correctly, two simulations are required: one with the",
++        "molecule of interest bound to its receptor and one with the molecule in water.",
++        "Both need to utilize [TT]energygrps[tt] such that Coul-SR(A-B), LJ-SR(A-B), etc. terms",
++        "are written to the [TT].edr[tt] file. Values from the molecule-in-water simulation",
++        "are necessary for supplying suitable values for -Elj and -Eqq."
 +    };
 +    static real        lie_lj = 0, lie_qq = 0, fac_lj = 0.181, fac_qq = 0.5;
 +    static const char *ligand = "none";
 +    t_pargs            pa[]   = {
 +        { "-Elj",  FALSE, etREAL, {&lie_lj},
 +          "Lennard-Jones interaction between ligand and solvent" },
 +        { "-Eqq",  FALSE, etREAL, {&lie_qq},
 +          "Coulomb interaction between ligand and solvent" },
 +        { "-Clj",  FALSE, etREAL, {&fac_lj},
 +          "Factor in the LIE equation for Lennard-Jones component of energy" },
 +        { "-Cqq",  FALSE, etREAL, {&fac_qq},
 +          "Factor in the LIE equation for Coulomb component of energy" },
 +        { "-ligand",  FALSE, etSTR, {&ligand},
 +          "Name of the ligand in the energy file" }
 +    };
 +#define NPA asize(pa)
 +
 +    FILE         *out;
 +    int           nre, nframes = 0, ct = 0;
 +    ener_file_t   fp;
 +    gmx_bool      bCont;
 +    t_liedata    *ld;
 +    gmx_enxnm_t  *enm = NULL;
 +    t_enxframe   *fr;
 +    real          lie;
 +    double        lieaver = 0, lieav2 = 0;
 +    output_env_t  oenv;
 +
 +    t_filenm      fnm[] = {
 +        { efEDR, "-f",    "ener",     ffREAD   },
 +        { efXVG, "-o",    "lie",      ffWRITE  }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    fp = open_enx(ftp2fn(efEDR, NFILE, fnm), "r");
 +    do_enxnms(fp, &nre, &enm);
 +
 +    ld = analyze_names(nre, enm, ligand);
 +    snew(fr, 1);
++
 +    out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "LIE free energy estimate",
 +                   "Time (ps)", "DGbind (kJ/mol)", oenv);
 +    while (do_enx(fp, fr))
 +    {
 +        ct = check_times(fr->t);
 +        if (ct == 0)
 +        {
 +            lie      = calc_lie(ld, fr->ener, lie_lj, lie_qq, fac_lj, fac_qq);
 +            lieaver += lie;
 +            lieav2  += lie*lie;
 +            nframes++;
 +            fprintf(out, "%10g  %10g\n", fr->t, lie);
 +        }
 +    }
 +    close_enx(fp);
 +    gmx_ffclose(out);
 +    fprintf(stderr, "\n");
 +
 +    if (nframes > 0)
 +    {
 +        printf("DGbind = %.3f (%.3f)\n", lieaver/nframes,
 +               sqrt(lieav2/nframes-sqr(lieaver/nframes)));
 +    }
 +
 +    do_view(oenv, ftp2fn(efXVG, NFILE, fnm), "-nxy");
 +
 +    return 0;
 +}
index e2d7a17e619ec75431ee630e49a3932dc65eb00a,0000000000000000000000000000000000000000..27a3b5b752d35180f8391b9a4ccccc75450cd397
mode 100644,000000..100644
--- /dev/null
@@@ -1,667 -1,0 +1,672 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "addconf.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdlib.h>
 +#include <string.h>
 +#include "vec.h"
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "types/commrec.h"
 +#include "force.h"
 +#include "names.h"
 +#include "nsgrid.h"
 +#include "mdatoms.h"
 +#include "nrnb.h"
 +#include "ns.h"
 +#include "mtop_util.h"
 +#include "chargegroup.h"
 +
 +static real box_margin;
 +
 +static real max_dist(rvec *x, real *r, int start, int end)
 +{
 +    real maxd;
 +    int  i, j;
 +
 +    maxd = 0;
 +    for (i = start; i < end; i++)
 +    {
 +        for (j = i+1; j < end; j++)
 +        {
 +            maxd = max(maxd, sqrt(distance2(x[i], x[j]))+0.5*(r[i]+r[j]));
 +        }
 +    }
 +
 +    return 0.5*maxd;
 +}
 +
 +static gmx_bool outside_box_minus_margin2(rvec x, matrix box)
 +{
 +    return ( (x[XX] < 2*box_margin) || (x[XX] > box[XX][XX]-2*box_margin) ||
 +             (x[YY] < 2*box_margin) || (x[YY] > box[YY][YY]-2*box_margin) ||
 +             (x[ZZ] < 2*box_margin) || (x[ZZ] > box[ZZ][ZZ]-2*box_margin) );
 +}
 +
 +static gmx_bool outside_box_plus_margin(rvec x, matrix box)
 +{
 +    return ( (x[XX] < -box_margin) || (x[XX] > box[XX][XX]+box_margin) ||
 +             (x[YY] < -box_margin) || (x[YY] > box[YY][YY]+box_margin) ||
 +             (x[ZZ] < -box_margin) || (x[ZZ] > box[ZZ][ZZ]+box_margin) );
 +}
 +
 +static int mark_res(int at, gmx_bool *mark, int natoms, t_atom *atom, int *nmark)
 +{
 +    int resind;
 +
 +    resind = atom[at].resind;
 +    while ( (at > 0) && (resind == atom[at-1].resind) )
 +    {
 +        at--;
 +    }
 +    while ( (at < natoms) && (resind == atom[at].resind) )
 +    {
 +        if (!mark[at])
 +        {
 +            mark[at] = TRUE;
 +            (*nmark)++;
 +        }
 +        at++;
 +    }
 +
 +    return at;
 +}
 +
 +static real find_max_real(int n, real radius[])
 +{
 +    int  i;
 +    real rmax;
 +
 +    rmax = 0;
 +    if (n > 0)
 +    {
 +        rmax = radius[0];
 +        for (i = 1; (i < n); i++)
 +        {
 +            rmax = max(rmax, radius[i]);
 +        }
 +    }
 +    return rmax;
 +}
 +
 +static void combine_atoms(t_atoms *ap, t_atoms *as,
 +                          rvec xp[], rvec *vp, rvec xs[], rvec *vs,
 +                          t_atoms **a_comb, rvec **x_comb, rvec **v_comb)
 +{
 +    t_atoms *ac;
 +    rvec    *xc, *vc = NULL;
 +    int      i, j, natot, res0;
 +
 +    /* Total number of atoms */
 +    natot = ap->nr+as->nr;
 +
 +    snew(ac, 1);
 +    init_t_atoms(ac, natot, FALSE);
 +
 +    snew(xc, natot);
 +    if (vp && vs)
 +    {
 +        snew(vc, natot);
 +    }
 +
 +    /* Fill the new structures */
 +    for (i = j = 0; (i < ap->nr); i++, j++)
 +    {
 +        copy_rvec(xp[i], xc[j]);
 +        if (vc)
 +        {
 +            copy_rvec(vp[i], vc[j]);
 +        }
 +        memcpy(&(ac->atom[j]), &(ap->atom[i]), sizeof(ap->atom[i]));
 +        ac->atom[j].type = 0;
 +    }
 +    res0 = ap->nres;
 +    for (i = 0; (i < as->nr); i++, j++)
 +    {
 +        copy_rvec(xs[i], xc[j]);
 +        if (vc)
 +        {
 +            copy_rvec(vs[i], vc[j]);
 +        }
 +        memcpy(&(ac->atom[j]), &(as->atom[i]), sizeof(as->atom[i]));
 +        ac->atom[j].type    = 0;
 +        ac->atom[j].resind += res0;
 +    }
 +    ac->nr   = j;
 +    ac->nres = ac->atom[j-1].resind+1;
 +    /* Fill all elements to prevent uninitialized memory */
 +    for (i = 0; i < ac->nr; i++)
 +    {
 +        ac->atom[i].m     = 1;
 +        ac->atom[i].q     = 0;
 +        ac->atom[i].mB    = 1;
 +        ac->atom[i].qB    = 0;
 +        ac->atom[i].type  = 0;
 +        ac->atom[i].typeB = 0;
 +        ac->atom[i].ptype = eptAtom;
 +    }
 +
 +    /* Return values */
 +    *a_comb = ac;
 +    *x_comb = xc;
 +    *v_comb = vc;
 +}
 +
 +static t_forcerec *fr = NULL;
 +
 +static void do_nsgrid(FILE *fp, gmx_bool bVerbose,
 +                      matrix box, rvec x[], t_atoms *atoms, real rlong,
 +                      const output_env_t oenv)
 +{
 +    gmx_mtop_t     *mtop;
 +    gmx_localtop_t *top;
 +    t_mdatoms      *md;
 +    t_block        *cgs;
 +    t_inputrec     *ir;
 +    t_nrnb          nrnb;
 +    t_commrec      *cr;
 +    int            *cg_index;
 +    gmx_moltype_t  *molt;
 +    gmx_ffparams_t *ffp;
 +    ivec           *nFreeze;
 +    int             i, m, natoms;
 +    rvec            box_size;
 +    real           *lambda, *dvdl;
 +
 +    natoms = atoms->nr;
 +
 +    /* Charge group index */
 +    snew(cg_index, natoms);
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        cg_index[i] = i;
 +    }
 +
 +    /* Topology needs charge groups and exclusions */
 +    snew(mtop, 1);
 +    init_mtop(mtop);
 +    mtop->natoms = natoms;
 +    /* Make one moltype that contains the whol system */
 +    mtop->nmoltype = 1;
 +    snew(mtop->moltype, mtop->nmoltype);
 +    molt        = &mtop->moltype[0];
 +    molt->name  = mtop->name;
 +    molt->atoms = *atoms;
 +    stupid_fill_block(&molt->cgs, mtop->natoms, FALSE);
 +    stupid_fill_blocka(&molt->excls, natoms);
 +    /* Make one molblock for the whole system */
 +    mtop->nmolblock = 1;
 +    snew(mtop->molblock, mtop->nmolblock);
 +    mtop->molblock[0].type       = 0;
 +    mtop->molblock[0].nmol       = 1;
 +    mtop->molblock[0].natoms_mol = natoms;
 +    /* Initialize a single energy group */
 +    mtop->groups.grps[egcENER].nr = 1;
 +    mtop->groups.ngrpnr[egcENER]  = 0;
 +    mtop->groups.grpnr[egcENER]   = NULL;
 +
 +    ffp = &mtop->ffparams;
 +
 +    ffp->ntypes = 1;
 +    ffp->atnr   = 1;
 +    ffp->reppow = 12;
 +    snew(ffp->functype, 1);
 +    snew(ffp->iparams, 1);
 +    ffp->iparams[0].lj.c6  = 1;
 +    ffp->iparams[0].lj.c12 = 1;
 +
 +    /* inputrec structure */
 +    snew(ir, 1);
 +    ir->cutoff_scheme    = ecutsGROUP;
 +    ir->coulomb_modifier = eintmodNONE;
 +    ir->vdw_modifier     = eintmodNONE;
 +    ir->coulombtype      = eelCUT;
 +    ir->vdwtype          = evdwCUT;
 +    ir->ndelta           = 2;
 +    ir->ns_type          = ensGRID;
 +    snew(ir->opts.egp_flags, 1);
 +
 +    top = gmx_mtop_generate_local_top(mtop, ir);
 +
 +    /* Some nasty shortcuts */
 +    cgs  = &(top->cgs);
 +
 +    /* mdatoms structure */
 +    snew(nFreeze, 2);
 +    snew(md, 1);
 +    md = init_mdatoms(fp, mtop, FALSE);
 +    atoms2md(mtop, ir, 0, NULL, mtop->natoms, md);
 +    sfree(nFreeze);
 +
 +    /* forcerec structure */
 +    if (fr == NULL)
 +    {
 +        fr = mk_forcerec();
 +    }
 +    snew(cr, 1);
 +    cr->nnodes   = 1;
 +    /* cr->nthreads = 1; */
 +
 +    /*    ir->rlist       = ir->rcoulomb = ir->rvdw = rlong;
 +       printf("Neighborsearching with a cut-off of %g\n",rlong);
 +       init_forcerec(stdout,fr,ir,top,cr,md,box,FALSE,NULL,NULL,NULL,TRUE);*/
 +    fr->cg0     = 0;
 +    fr->hcg     = top->cgs.nr;
 +    fr->nWatMol = 0;
 +
 +    /* Prepare for neighboursearching */
 +    init_nrnb(&nrnb);
 +
 +    /* Init things dependent on parameters */
 +    ir->rlistlong = ir->rlist = ir->rcoulomb = ir->rvdw = rlong;
 +    /* create free energy data to avoid NULLs */
 +    snew(ir->fepvals, 1);
 +    printf("Neighborsearching with a cut-off of %g\n", rlong);
 +    init_forcerec(stdout, oenv, fr, NULL, ir, mtop, cr, box,
 +                  NULL, NULL, NULL, NULL, NULL, TRUE, -1);
 +    if (debug)
 +    {
 +        pr_forcerec(debug, fr);
 +    }
 +
 +    /* Calculate new stuff dependent on coords and box */
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        box_size[m] = box[m][m];
 +    }
 +    calc_shifts(box, fr->shift_vec);
 +    put_charge_groups_in_box(fp, 0, cgs->nr, fr->ePBC, box, cgs, x, fr->cg_cm);
 +
 +    /* Do the actual neighboursearching */
 +    snew(lambda, efptNR);
 +    snew(dvdl, efptNR);
 +    init_neighbor_list(fp, fr, md->homenr);
 +    search_neighbours(fp, fr, box, top,
 +                      &mtop->groups, cr, &nrnb, md, TRUE, FALSE);
 +
 +    if (debug)
 +    {
 +        dump_nblist(debug, cr, fr, 0);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Successfully made neighbourlist\n");
 +    }
 +}
 +
 +static gmx_bool bXor(gmx_bool b1, gmx_bool b2)
 +{
 +    return (b1 && !b2) || (b2 && !b1);
 +}
 +
 +void add_conf(t_atoms *atoms, rvec **x, rvec **v, real **r, gmx_bool bSrenew,
 +              int ePBC, matrix box, gmx_bool bInsert,
 +              t_atoms *atoms_solvt, rvec *x_solvt, rvec *v_solvt, real *r_solvt,
 +              gmx_bool bVerbose, real rshell, int max_sol, const output_env_t oenv)
 +{
 +    t_nblist       *nlist;
 +    t_atoms        *atoms_all;
 +    real            max_vdw, *r_prot, *r_all, n2, r2, ib1, ib2;
 +    int             natoms_prot, natoms_solvt;
 +    int             i, j, jj, m, j0, j1, jjj, jnres, jnr, inr, iprot, is1, is2;
 +    int             prev, resnr, nresadd, d, k, ncells, maxincell;
 +    int             dx0, dx1, dy0, dy1, dz0, dz1;
 +    int             ntest, nremove, nkeep;
 +    rvec            dx, xi, xj, xpp, *x_all, *v_all;
 +    gmx_bool       *remove, *keep;
 +    int             bSolSol;
 +
 +    natoms_prot  = atoms->nr;
 +    natoms_solvt = atoms_solvt->nr;
 +    if (natoms_solvt <= 0)
 +    {
 +        fprintf(stderr, "WARNING: Nothing to add\n");
 +        return;
 +    }
 +
 +    if (ePBC == epbcSCREW)
 +    {
 +        gmx_fatal(FARGS, "Sorry, %s pbc is not yet supported", epbc_names[ePBC]);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Calculating Overlap...\n");
 +    }
 +
 +    /* Set margin around box edges to largest solvent dimension.
 +     * The maximum distance between atoms in a solvent molecule should
 +     * be calculated. At the moment a fudge factor of 3 is used.
 +     */
 +    r_prot     = *r;
 +    box_margin = 3*find_max_real(natoms_solvt, r_solvt);
 +    max_vdw    = max(3*find_max_real(natoms_prot, r_prot), box_margin);
 +    fprintf(stderr, "box_margin = %g\n", box_margin);
 +
 +    snew(remove, natoms_solvt);
 +
 +    nremove = 0;
 +    if (!bInsert)
 +    {
 +        for (i = 0; i < atoms_solvt->nr; i++)
 +        {
 +            if (outside_box_plus_margin(x_solvt[i], box) )
 +            {
 +                i = mark_res(i, remove, atoms_solvt->nr, atoms_solvt->atom, &nremove);
 +            }
 +        }
 +        fprintf(stderr, "Removed %d atoms that were outside the box\n", nremove);
 +    }
 +
 +    /* Define grid stuff */
 +    /* Largest VDW radius */
 +    snew(r_all, natoms_prot+natoms_solvt);
 +    for (i = j = 0; i < natoms_prot; i++, j++)
 +    {
 +        r_all[j] = r_prot[i];
 +    }
 +    for (i = 0; i < natoms_solvt; i++, j++)
 +    {
 +        r_all[j] = r_solvt[i];
 +    }
 +
 +    /* Combine arrays */
 +    combine_atoms(atoms, atoms_solvt, *x, v ? *v : NULL, x_solvt, v_solvt,
 +                  &atoms_all, &x_all, &v_all);
 +
 +    /* Do neighboursearching step */
 +    do_nsgrid(stdout, bVerbose, box, x_all, atoms_all, max_vdw, oenv);
 +
 +    /* check solvent with solute */
 +    nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]);
 +    fprintf(stderr, "nri = %d, nrj = %d\n", nlist->nri, nlist->nrj);
 +    for (bSolSol = 0; (bSolSol <= (bInsert ? 0 : 1)); bSolSol++)
 +    {
 +        ntest = nremove = 0;
 +        fprintf(stderr, "Checking %s-Solvent overlap:",
 +                bSolSol ? "Solvent" : "Protein");
 +        for (i = 0; (i < nlist->nri && nremove < natoms_solvt); i++)
 +        {
 +            inr = nlist->iinr[i];
 +            j0  = nlist->jindex[i];
 +            j1  = nlist->jindex[i+1];
 +            rvec_add(x_all[inr], fr->shift_vec[nlist->shift[i]], xi);
 +
 +            for (j = j0; (j < j1 && nremove < natoms_solvt); j++)
 +            {
 +                jnr = nlist->jjnr[j];
++                if (jnr < 0)
++                {
++                    /* skip padding */
++                    continue;
++                }
 +                copy_rvec(x_all[jnr], xj);
 +
 +                /* Check solvent-protein and solvent-solvent */
 +                is1 = inr-natoms_prot;
 +                is2 = jnr-natoms_prot;
 +
 +                /* Check if at least one of the atoms is a solvent that is not yet
 +                 * listed for removal, and if both are solvent, that they are not in the
 +                 * same residue.
 +                 */
 +                if ((!bSolSol &&
 +                     bXor((is1 >= 0), (is2 >= 0)) && /* One atom is protein */
 +                     ((is1 < 0) || ((is1 >= 0) && !remove[is1])) &&
 +                     ((is2 < 0) || ((is2 >= 0) && !remove[is2]))) ||
 +
 +                    (bSolSol  &&
 +                     (is1 >= 0) && (!remove[is1]) &&                   /* is1 is solvent */
 +                     (is2 >= 0) && (!remove[is2]) &&                   /* is2 is solvent */
 +                     (bInsert ||                                       /* when inserting also check inside the box */
 +                      (outside_box_minus_margin2(x_solvt[is1], box) && /* is1 on edge */
 +                       outside_box_minus_margin2(x_solvt[is2], box))   /* is2 on edge */
 +                     ) &&
 +                     (atoms_solvt->atom[is1].resind !=                 /* Not the same residue */
 +                      atoms_solvt->atom[is2].resind)))
 +                {
 +
 +                    ntest++;
 +                    rvec_sub(xi, xj, dx);
 +                    n2 = norm2(dx);
 +                    r2 = sqr(r_all[inr]+r_all[jnr]);
 +                    if (n2 < r2)
 +                    {
 +                        if (bInsert)
 +                        {
 +                            nremove = natoms_solvt;
 +                            for (k = 0; k < nremove; k++)
 +                            {
 +                                remove[k] = TRUE;
 +                            }
 +                        }
 +                        /* Need only remove one of the solvents... */
 +                        if (is2 >= 0)
 +                        {
 +                            (void) mark_res(is2, remove, natoms_solvt, atoms_solvt->atom,
 +                                            &nremove);
 +                        }
 +                        else if (is1 >= 0)
 +                        {
 +                            (void) mark_res(is1, remove, natoms_solvt, atoms_solvt->atom,
 +                                            &nremove);
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "Neither atom is solvent%d %d\n", is1, is2);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        if (!bInsert)
 +        {
 +            fprintf(stderr, " tested %d pairs, removed %d atoms.\n", ntest, nremove);
 +        }
 +    }
 +    if (debug)
 +    {
 +        for (i = 0; i < natoms_solvt; i++)
 +        {
 +            fprintf(debug, "remove[%5d] = %s\n", i, bool_names[remove[i]]);
 +        }
 +    }
 +
 +    /* Search again, now with another cut-off */
 +    if (rshell > 0)
 +    {
 +        do_nsgrid(stdout, bVerbose, box, x_all, atoms_all, rshell, oenv);
 +        nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]);
 +        fprintf(stderr, "nri = %d, nrj = %d\n", nlist->nri, nlist->nrj);
 +        nkeep = 0;
 +        snew(keep, natoms_solvt);
 +        for (i = 0; i < nlist->nri; i++)
 +        {
 +            inr = nlist->iinr[i];
 +            j0  = nlist->jindex[i];
 +            j1  = nlist->jindex[i+1];
 +
 +            for (j = j0; j < j1; j++)
 +            {
 +                jnr = nlist->jjnr[j];
 +
 +                /* Check solvent-protein and solvent-solvent */
 +                is1 = inr-natoms_prot;
 +                is2 = jnr-natoms_prot;
 +
 +                /* Check if at least one of the atoms is a solvent that is not yet
 +                 * listed for removal, and if both are solvent, that they are not in the
 +                 * same residue.
 +                 */
 +                if (is1 >= 0 && is2 < 0)
 +                {
 +                    mark_res(is1, keep, natoms_solvt, atoms_solvt->atom, &nkeep);
 +                }
 +                else if (is1 < 0 && is2 >= 0)
 +                {
 +                    mark_res(is2, keep, natoms_solvt, atoms_solvt->atom, &nkeep);
 +                }
 +            }
 +        }
 +        fprintf(stderr, "Keeping %d solvent atoms after proximity check\n",
 +                nkeep);
 +        for (i = 0; i < natoms_solvt; i++)
 +        {
 +            remove[i] = remove[i] || !keep[i];
 +        }
 +        sfree(keep);
 +    }
 +    /* count how many atoms and residues will be added and make space */
 +    if (bInsert)
 +    {
 +        j     = atoms_solvt->nr;
 +        jnres = atoms_solvt->nres;
 +    }
 +    else
 +    {
 +        j     = 0;
 +        jnres = 0;
 +        for (i = 0; ((i < atoms_solvt->nr) &&
 +                     ((max_sol == 0) || (jnres < max_sol))); i++)
 +        {
 +            if (!remove[i])
 +            {
 +                j++;
 +                if ((i == 0) ||
 +                    (atoms_solvt->atom[i].resind != atoms_solvt->atom[i-1].resind))
 +                {
 +                    jnres++;
 +                }
 +            }
 +        }
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Will add %d atoms in %d residues\n", j, jnres);
 +    }
 +    if (!bInsert)
 +    {
 +        /* Flag the remaing solvent atoms to be removed */
 +        jjj = atoms_solvt->atom[i-1].resind;
 +        for (; (i < atoms_solvt->nr); i++)
 +        {
 +            if (atoms_solvt->atom[i].resind > jjj)
 +            {
 +                remove[i] = TRUE;
 +            }
 +            else
 +            {
 +                j++;
 +            }
 +        }
 +    }
 +
 +    if (bSrenew)
 +    {
 +        srenew(atoms->resinfo,  atoms->nres+jnres);
 +        srenew(atoms->atomname, atoms->nr+j);
 +        srenew(atoms->atom,     atoms->nr+j);
 +        srenew(*x,              atoms->nr+j);
 +        if (v)
 +        {
 +            srenew(*v,       atoms->nr+j);
 +        }
 +        srenew(*r,              atoms->nr+j);
 +    }
 +
 +    /* add the selected atoms_solvt to atoms */
 +    if (atoms->nr > 0)
 +    {
 +        resnr = atoms->resinfo[atoms->atom[atoms->nr-1].resind].nr;
 +    }
 +    else
 +    {
 +        resnr = 0;
 +    }
 +    prev    = -1;
 +    nresadd = 0;
 +    for (i = 0; i < atoms_solvt->nr; i++)
 +    {
 +        if (!remove[i])
 +        {
 +            if (prev == -1 ||
 +                atoms_solvt->atom[i].resind != atoms_solvt->atom[prev].resind)
 +            {
 +                nresadd++;
 +                atoms->nres++;
 +                resnr++;
 +                atoms->resinfo[atoms->nres-1] =
 +                    atoms_solvt->resinfo[atoms_solvt->atom[i].resind];
 +                atoms->resinfo[atoms->nres-1].nr = resnr;
 +                /* calculate shift of the solvent molecule using the first atom */
 +                copy_rvec(x_solvt[i], dx);
 +                put_atoms_in_box(ePBC, box, 1, &dx);
 +                rvec_dec(dx, x_solvt[i]);
 +            }
 +            atoms->atom[atoms->nr]     = atoms_solvt->atom[i];
 +            atoms->atomname[atoms->nr] = atoms_solvt->atomname[i];
 +            rvec_add(x_solvt[i], dx, (*x)[atoms->nr]);
 +            if (v)
 +            {
 +                copy_rvec(v_solvt[i], (*v)[atoms->nr]);
 +            }
 +            (*r)[atoms->nr]               = r_solvt[i];
 +            atoms->atom[atoms->nr].resind = atoms->nres-1;
 +            atoms->nr++;
 +            prev = i;
 +        }
 +    }
 +    if (bSrenew)
 +    {
 +        srenew(atoms->resinfo,  atoms->nres+nresadd);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Added %d molecules\n", nresadd);
 +    }
 +
 +    sfree(remove);
 +    done_atom(atoms_all);
 +    sfree(x_all);
 +    sfree(v_all);
 +}
index 872c670990c99807997386445ec424ecab86cc7e,0000000000000000000000000000000000000000..b562b5188fd5bc4dbdcb96297521fd9558395e56
mode 100644,000000..100644
--- /dev/null
@@@ -1,2054 -1,0 +1,2059 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "grompp.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <sys/types.h>
 +#include <math.h>
 +#include <string.h>
 +#include <errno.h>
 +#include <limits.h>
 +#include <assert.h>
 +
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "readir.h"
 +#include "toputil.h"
 +#include "topio.h"
 +#include "gromacs/fileio/confio.h"
 +#include "readir.h"
 +#include "symtab.h"
 +#include "names.h"
 +#include "grompp-impl.h"
 +#include "gromacs/random/random.h"
 +#include "gromacs/gmxpreprocess/gen_maxwell_velocities.h"
 +#include "vec.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "splitter.h"
 +#include "gromacs/gmxpreprocess/sortwater.h"
 +#include "convparm.h"
 +#include "gmx_fatal.h"
 +#include "warninp.h"
 +#include "index.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "vsite_parm.h"
 +#include "txtdump.h"
 +#include "calcgrid.h"
 +#include "add_par.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "perf_est.h"
 +#include "compute_io.h"
 +#include "gpp_atomtype.h"
 +#include "mtop_util.h"
 +#include "genborn.h"
 +#include "calc_verletbuf.h"
 +#include "tomorse.h"
 +#include "gromacs/imd/imd.h"
 +
 +
 +static int rm_interactions(int ifunc, int nrmols, t_molinfo mols[])
 +{
 +    int  i, n;
 +
 +    n = 0;
 +    /* For all the molecule types */
 +    for (i = 0; i < nrmols; i++)
 +    {
 +        n += mols[i].plist[ifunc].nr;
 +        mols[i].plist[ifunc].nr = 0;
 +    }
 +    return n;
 +}
 +
 +static int check_atom_names(const char *fn1, const char *fn2,
 +                            gmx_mtop_t *mtop, t_atoms *at)
 +{
 +    int      mb, m, i, j, nmismatch;
 +    t_atoms *tat;
 +#define MAXMISMATCH 20
 +
 +    if (mtop->natoms != at->nr)
 +    {
 +        gmx_incons("comparing atom names");
 +    }
 +
 +    nmismatch = 0;
 +    i         = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        tat = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +        for (m = 0; m < mtop->molblock[mb].nmol; m++)
 +        {
 +            for (j = 0; j < tat->nr; j++)
 +            {
 +                if (strcmp( *(tat->atomname[j]), *(at->atomname[i]) ) != 0)
 +                {
 +                    if (nmismatch < MAXMISMATCH)
 +                    {
 +                        fprintf(stderr,
 +                                "Warning: atom name %d in %s and %s does not match (%s - %s)\n",
 +                                i+1, fn1, fn2, *(tat->atomname[j]), *(at->atomname[i]));
 +                    }
 +                    else if (nmismatch == MAXMISMATCH)
 +                    {
 +                        fprintf(stderr, "(more than %d non-matching atom names)\n", MAXMISMATCH);
 +                    }
 +                    nmismatch++;
 +                }
 +                i++;
 +            }
 +        }
 +    }
 +
 +    return nmismatch;
 +}
 +
 +static void check_eg_vs_cg(gmx_mtop_t *mtop)
 +{
 +    int            astart, mb, m, cg, j, firstj;
 +    unsigned char  firsteg, eg;
 +    gmx_moltype_t *molt;
 +
 +    /* Go through all the charge groups and make sure all their
 +     * atoms are in the same energy group.
 +     */
 +
 +    astart = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +        for (m = 0; m < mtop->molblock[mb].nmol; m++)
 +        {
 +            for (cg = 0; cg < molt->cgs.nr; cg++)
 +            {
 +                /* Get the energy group of the first atom in this charge group */
 +                firstj  = astart + molt->cgs.index[cg];
 +                firsteg = ggrpnr(&mtop->groups, egcENER, firstj);
 +                for (j = molt->cgs.index[cg]+1; j < molt->cgs.index[cg+1]; j++)
 +                {
 +                    eg = ggrpnr(&mtop->groups, egcENER, astart+j);
 +                    if (eg != firsteg)
 +                    {
 +                        gmx_fatal(FARGS, "atoms %d and %d in charge group %d of molecule type '%s' are in different energy groups",
 +                                  firstj+1, astart+j+1, cg+1, *molt->name);
 +                    }
 +                }
 +            }
 +            astart += molt->atoms.nr;
 +        }
 +    }
 +}
 +
 +static void check_cg_sizes(const char *topfn, t_block *cgs, warninp_t wi)
 +{
 +    int  maxsize, cg;
 +    char warn_buf[STRLEN];
 +
 +    maxsize = 0;
 +    for (cg = 0; cg < cgs->nr; cg++)
 +    {
 +        maxsize = max(maxsize, cgs->index[cg+1]-cgs->index[cg]);
 +    }
 +
 +    if (maxsize > MAX_CHARGEGROUP_SIZE)
 +    {
 +        gmx_fatal(FARGS, "The largest charge group contains %d atoms. The maximum is %d.", maxsize, MAX_CHARGEGROUP_SIZE);
 +    }
 +    else if (maxsize > 10)
 +    {
 +        set_warning_line(wi, topfn, -1);
 +        sprintf(warn_buf,
 +                "The largest charge group contains %d atoms.\n"
 +                "Since atoms only see each other when the centers of geometry of the charge groups they belong to are within the cut-off distance, too large charge groups can lead to serious cut-off artifacts.\n"
 +                "For efficiency and accuracy, charge group should consist of a few atoms.\n"
 +                "For all-atom force fields use: CH3, CH2, CH, NH2, NH, OH, CO2, CO, etc.",
 +                maxsize);
 +        warning_note(wi, warn_buf);
 +    }
 +}
 +
 +static void check_bonds_timestep(gmx_mtop_t *mtop, double dt, warninp_t wi)
 +{
 +    /* This check is not intended to ensure accurate integration,
 +     * rather it is to signal mistakes in the mdp settings.
 +     * A common mistake is to forget to turn on constraints
 +     * for MD after energy minimization with flexible bonds.
 +     * This check can also detect too large time steps for flexible water
 +     * models, but such errors will often be masked by the constraints
 +     * mdp options, which turns flexible water into water with bond constraints,
 +     * but without an angle constraint. Unfortunately such incorrect use
 +     * of water models can not easily be detected without checking
 +     * for specific model names.
 +     *
 +     * The stability limit of leap-frog or velocity verlet is 4.44 steps
 +     * per oscillational period.
 +     * But accurate bonds distributions are lost far before that limit.
 +     * To allow relatively common schemes (although not common with Gromacs)
 +     * of dt=1 fs without constraints and dt=2 fs with only H-bond constraints
 +     * we set the note limit to 10.
 +     */
 +    int            min_steps_warn = 5;
 +    int            min_steps_note = 10;
 +    t_iparams     *ip;
 +    int            molt;
 +    gmx_moltype_t *moltype, *w_moltype;
 +    t_atom        *atom;
 +    t_ilist       *ilist, *ilb, *ilc, *ils;
 +    int            ftype;
 +    int            i, a1, a2, w_a1, w_a2, j;
 +    real           twopi2, limit2, fc, re, m1, m2, period2, w_period2;
 +    gmx_bool       bFound, bWater, bWarn;
 +    char           warn_buf[STRLEN];
 +
 +    ip = mtop->ffparams.iparams;
 +
 +    twopi2 = sqr(2*M_PI);
 +
 +    limit2 = sqr(min_steps_note*dt);
 +
 +    w_a1      = w_a2 = -1;
 +    w_period2 = -1.0;
 +
 +    w_moltype = NULL;
 +    for (molt = 0; molt < mtop->nmoltype; molt++)
 +    {
 +        moltype = &mtop->moltype[molt];
 +        atom    = moltype->atoms.atom;
 +        ilist   = moltype->ilist;
 +        ilc     = &ilist[F_CONSTR];
 +        ils     = &ilist[F_SETTLE];
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (!(ftype == F_BONDS || ftype == F_G96BONDS || ftype == F_HARMONIC))
 +            {
 +                continue;
 +            }
 +
 +            ilb = &ilist[ftype];
 +            for (i = 0; i < ilb->nr; i += 3)
 +            {
 +                fc = ip[ilb->iatoms[i]].harmonic.krA;
 +                re = ip[ilb->iatoms[i]].harmonic.rA;
 +                if (ftype == F_G96BONDS)
 +                {
 +                    /* Convert squared sqaure fc to harmonic fc */
 +                    fc = 2*fc*re;
 +                }
 +                a1 = ilb->iatoms[i+1];
 +                a2 = ilb->iatoms[i+2];
 +                m1 = atom[a1].m;
 +                m2 = atom[a2].m;
 +                if (fc > 0 && m1 > 0 && m2 > 0)
 +                {
 +                    period2 = twopi2*m1*m2/((m1 + m2)*fc);
 +                }
 +                else
 +                {
 +                    period2 = GMX_FLOAT_MAX;
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "fc %g m1 %g m2 %g period %g\n",
 +                            fc, m1, m2, sqrt(period2));
 +                }
 +                if (period2 < limit2)
 +                {
 +                    bFound = FALSE;
 +                    for (j = 0; j < ilc->nr; j += 3)
 +                    {
 +                        if ((ilc->iatoms[j+1] == a1 && ilc->iatoms[j+2] == a2) ||
 +                            (ilc->iatoms[j+1] == a2 && ilc->iatoms[j+2] == a1))
 +                        {
 +                            bFound = TRUE;
 +                        }
 +                    }
 +                    for (j = 0; j < ils->nr; j += 4)
 +                    {
 +                        if ((a1 == ils->iatoms[j+1] || a1 == ils->iatoms[j+2] || a1 == ils->iatoms[j+3]) &&
 +                            (a2 == ils->iatoms[j+1] || a2 == ils->iatoms[j+2] || a2 == ils->iatoms[j+3]))
 +                        {
 +                            bFound = TRUE;
 +                        }
 +                    }
 +                    if (!bFound &&
 +                        (w_moltype == NULL || period2 < w_period2))
 +                    {
 +                        w_moltype = moltype;
 +                        w_a1      = a1;
 +                        w_a2      = a2;
 +                        w_period2 = period2;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (w_moltype != NULL)
 +    {
 +        bWarn = (w_period2 < sqr(min_steps_warn*dt));
 +        /* A check that would recognize most water models */
 +        bWater = ((*w_moltype->atoms.atomname[0])[0] == 'O' &&
 +                  w_moltype->atoms.nr <= 5);
 +        sprintf(warn_buf, "The bond in molecule-type %s between atoms %d %s and %d %s has an estimated oscillational period of %.1e ps, which is less than %d times the time step of %.1e ps.\n"
 +                "%s",
 +                *w_moltype->name,
 +                w_a1+1, *w_moltype->atoms.atomname[w_a1],
 +                w_a2+1, *w_moltype->atoms.atomname[w_a2],
 +                sqrt(w_period2), bWarn ? min_steps_warn : min_steps_note, dt,
 +                bWater ?
 +                "Maybe you asked for fexible water." :
 +                "Maybe you forgot to change the constraints mdp option.");
 +        if (bWarn)
 +        {
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static void check_vel(gmx_mtop_t *mtop, rvec v[])
 +{
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     a;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        if (atom->ptype == eptShell ||
 +            atom->ptype == eptBond  ||
 +            atom->ptype == eptVSite)
 +        {
 +            clear_rvec(v[a]);
 +        }
 +    }
 +}
 +
 +static gmx_bool nint_ftype(gmx_mtop_t *mtop, t_molinfo *mi, int ftype)
 +{
 +    int nint, mb;
 +
 +    nint = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        nint += mtop->molblock[mb].nmol*mi[mtop->molblock[mb].type].plist[ftype].nr;
 +    }
 +
 +    return nint;
 +}
 +
 +/* This routine reorders the molecule type array
 + * in the order of use in the molblocks,
 + * unused molecule types are deleted.
 + */
 +static void renumber_moltypes(gmx_mtop_t *sys,
 +                              int *nmolinfo, t_molinfo **molinfo)
 +{
 +    int       *order, norder, i;
 +    int        mb, mi;
 +    t_molinfo *minew;
 +
 +    snew(order, *nmolinfo);
 +    norder = 0;
 +    for (mb = 0; mb < sys->nmolblock; mb++)
 +    {
 +        for (i = 0; i < norder; i++)
 +        {
 +            if (order[i] == sys->molblock[mb].type)
 +            {
 +                break;
 +            }
 +        }
 +        if (i == norder)
 +        {
 +            /* This type did not occur yet, add it */
 +            order[norder] = sys->molblock[mb].type;
 +            /* Renumber the moltype in the topology */
 +            norder++;
 +        }
 +        sys->molblock[mb].type = i;
 +    }
 +
 +    /* We still need to reorder the molinfo structs */
 +    snew(minew, norder);
 +    for (mi = 0; mi < *nmolinfo; mi++)
 +    {
 +        for (i = 0; i < norder; i++)
 +        {
 +            if (order[i] == mi)
 +            {
 +                break;
 +            }
 +        }
 +        if (i == norder)
 +        {
 +            done_mi(&(*molinfo)[mi]);
 +        }
 +        else
 +        {
 +            minew[i] = (*molinfo)[mi];
 +        }
 +    }
 +    sfree(*molinfo);
 +
 +    *nmolinfo = norder;
 +    *molinfo  = minew;
 +}
 +
 +static void molinfo2mtop(int nmi, t_molinfo *mi, gmx_mtop_t *mtop)
 +{
 +    int            m;
 +    gmx_moltype_t *molt;
 +
 +    mtop->nmoltype = nmi;
 +    snew(mtop->moltype, nmi);
 +    for (m = 0; m < nmi; m++)
 +    {
 +        molt        = &mtop->moltype[m];
 +        molt->name  = mi[m].name;
 +        molt->atoms = mi[m].atoms;
 +        /* ilists are copied later */
 +        molt->cgs   = mi[m].cgs;
 +        molt->excls = mi[m].excls;
 +    }
 +}
 +
 +static void
 +new_status(const char *topfile, const char *topppfile, const char *confin,
 +           t_gromppopts *opts, t_inputrec *ir, gmx_bool bZero,
 +           gmx_bool bGenVel, gmx_bool bVerbose, t_state *state,
 +           gpp_atomtype_t atype, gmx_mtop_t *sys,
 +           int *nmi, t_molinfo **mi, t_params plist[],
 +           int *comb, double *reppow, real *fudgeQQ,
 +           gmx_bool bMorse,
 +           warninp_t wi)
 +{
 +    t_molinfo      *molinfo = NULL;
 +    int             nmolblock;
 +    gmx_molblock_t *molblock, *molbs;
 +    t_atoms        *confat;
 +    int             mb, i, nrmols, nmismatch;
 +    char            buf[STRLEN];
 +    gmx_bool        bGB = FALSE;
 +    char            warn_buf[STRLEN];
 +
 +    init_mtop(sys);
 +
 +    /* Set gmx_boolean for GB */
 +    if (ir->implicit_solvent)
 +    {
 +        bGB = TRUE;
 +    }
 +
 +    /* TOPOLOGY processing */
 +    sys->name = do_top(bVerbose, topfile, topppfile, opts, bZero, &(sys->symtab),
 +                       plist, comb, reppow, fudgeQQ,
 +                       atype, &nrmols, &molinfo, ir,
 +                       &nmolblock, &molblock, bGB,
 +                       wi);
 +
 +    sys->nmolblock = 0;
 +    snew(sys->molblock, nmolblock);
 +
 +    sys->natoms = 0;
 +    for (mb = 0; mb < nmolblock; mb++)
 +    {
 +        if (sys->nmolblock > 0 &&
 +            molblock[mb].type == sys->molblock[sys->nmolblock-1].type)
 +        {
 +            /* Merge consecutive blocks with the same molecule type */
 +            sys->molblock[sys->nmolblock-1].nmol += molblock[mb].nmol;
 +            sys->natoms += molblock[mb].nmol*sys->molblock[sys->nmolblock-1].natoms_mol;
 +        }
 +        else if (molblock[mb].nmol > 0)
 +        {
 +            /* Add a new molblock to the topology */
 +            molbs             = &sys->molblock[sys->nmolblock];
 +            *molbs            = molblock[mb];
 +            molbs->natoms_mol = molinfo[molbs->type].atoms.nr;
 +            molbs->nposres_xA = 0;
 +            molbs->nposres_xB = 0;
 +            sys->natoms      += molbs->nmol*molbs->natoms_mol;
 +            sys->nmolblock++;
 +        }
 +    }
 +    if (sys->nmolblock == 0)
 +    {
 +        gmx_fatal(FARGS, "No molecules were defined in the system");
 +    }
 +
 +    renumber_moltypes(sys, &nrmols, &molinfo);
 +
 +    if (bMorse)
 +    {
 +        convert_harmonics(nrmols, molinfo, atype);
 +    }
 +
 +    if (ir->eDisre == edrNone)
 +    {
 +        i = rm_interactions(F_DISRES, nrmols, molinfo);
 +        if (i > 0)
 +        {
 +            set_warning_line(wi, "unknown", -1);
 +            sprintf(warn_buf, "disre = no, removed %d distance restraints", i);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +    if (opts->bOrire == FALSE)
 +    {
 +        i = rm_interactions(F_ORIRES, nrmols, molinfo);
 +        if (i > 0)
 +        {
 +            set_warning_line(wi, "unknown", -1);
 +            sprintf(warn_buf, "orire = no, removed %d orientation restraints", i);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    /* Copy structures from msys to sys */
 +    molinfo2mtop(nrmols, molinfo, sys);
 +
 +    gmx_mtop_finalize(sys);
 +
 +    /* COORDINATE file processing */
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing coordinates...\n");
 +    }
 +
 +    get_stx_coordnum(confin, &state->natoms);
 +    if (state->natoms != sys->natoms)
 +    {
 +        gmx_fatal(FARGS, "number of coordinates in coordinate file (%s, %d)\n"
 +                  "             does not match topology (%s, %d)",
 +                  confin, state->natoms, topfile, sys->natoms);
 +    }
 +    else
 +    {
 +        /* make space for coordinates and velocities */
 +        char title[STRLEN];
 +        snew(confat, 1);
 +        init_t_atoms(confat, state->natoms, FALSE);
 +        init_state(state, state->natoms, 0, 0, 0, 0);
 +        read_stx_conf(confin, title, confat, state->x, state->v, NULL, state->box);
 +        /* This call fixes the box shape for runs with pressure scaling */
 +        set_box_rel(ir, state);
 +
 +        nmismatch = check_atom_names(topfile, confin, sys, confat);
 +        free_t_atoms(confat, TRUE);
 +        sfree(confat);
 +
 +        if (nmismatch)
 +        {
 +            sprintf(buf, "%d non-matching atom name%s\n"
 +                    "atom names from %s will be used\n"
 +                    "atom names from %s will be ignored\n",
 +                    nmismatch, (nmismatch == 1) ? "" : "s", topfile, confin);
 +            warning(wi, buf);
 +        }
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "double-checking input for internal consistency...\n");
 +        }
 +        double_check(ir, state->box, nint_ftype(sys, molinfo, F_CONSTR), wi);
 +    }
 +
 +    if (bGenVel)
 +    {
 +        real                   *mass;
 +        gmx_mtop_atomloop_all_t aloop;
 +        t_atom                 *atom;
 +        unsigned int            useed;
 +
 +        snew(mass, state->natoms);
 +        aloop = gmx_mtop_atomloop_all_init(sys);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            mass[i] = atom->m;
 +        }
 +
 +        useed = opts->seed;
 +        if (opts->seed == -1)
 +        {
 +            useed = (int)gmx_rng_make_seed();
 +            fprintf(stderr, "Setting gen_seed to %u\n", useed);
 +        }
 +        maxwell_speed(opts->tempi, useed, sys, state->v);
 +
 +        stop_cm(stdout, state->natoms, mass, state->x, state->v);
 +        sfree(mass);
 +    }
 +
 +    *nmi = nrmols;
 +    *mi  = molinfo;
 +}
 +
 +static void copy_state(const char *slog, t_trxframe *fr,
 +                       gmx_bool bReadVel, t_state *state,
 +                       double *use_time)
 +{
 +    int i;
 +
 +    if (fr->not_ok & FRAME_NOT_OK)
 +    {
 +        gmx_fatal(FARGS, "Can not start from an incomplete frame");
 +    }
 +    if (!fr->bX)
 +    {
 +        gmx_fatal(FARGS, "Did not find a frame with coordinates in file %s",
 +                  slog);
 +    }
 +
 +    for (i = 0; i < state->natoms; i++)
 +    {
 +        copy_rvec(fr->x[i], state->x[i]);
 +    }
 +    if (bReadVel)
 +    {
 +        if (!fr->bV)
 +        {
 +            gmx_incons("Trajecory frame unexpectedly does not contain velocities");
 +        }
 +        for (i = 0; i < state->natoms; i++)
 +        {
 +            copy_rvec(fr->v[i], state->v[i]);
 +        }
 +    }
 +    if (fr->bBox)
 +    {
 +        copy_mat(fr->box, state->box);
 +    }
 +
 +    *use_time = fr->time;
 +}
 +
 +static void cont_status(const char *slog, const char *ener,
 +                        gmx_bool bNeedVel, gmx_bool bGenVel, real fr_time,
 +                        t_inputrec *ir, t_state *state,
 +                        gmx_mtop_t *sys,
 +                        const output_env_t oenv)
 +/* If fr_time == -1 read the last frame available which is complete */
 +{
 +    gmx_bool     bReadVel;
 +    t_trxframe   fr;
 +    t_trxstatus *fp;
 +    int          i;
 +    double       use_time;
 +
 +    bReadVel = (bNeedVel && !bGenVel);
 +
 +    fprintf(stderr,
 +            "Reading Coordinates%s and Box size from old trajectory\n",
 +            bReadVel ? ", Velocities" : "");
 +    if (fr_time == -1)
 +    {
 +        fprintf(stderr, "Will read whole trajectory\n");
 +    }
 +    else
 +    {
 +        fprintf(stderr, "Will read till time %g\n", fr_time);
 +    }
 +    if (!bReadVel)
 +    {
 +        if (bGenVel)
 +        {
 +            fprintf(stderr, "Velocities generated: "
 +                    "ignoring velocities in input trajectory\n");
 +        }
 +        read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
 +    }
 +    else
 +    {
 +        read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X | TRX_NEED_V);
 +
 +        if (!fr.bV)
 +        {
 +            fprintf(stderr,
 +                    "\n"
 +                    "WARNING: Did not find a frame with velocities in file %s,\n"
 +                    "         all velocities will be set to zero!\n\n", slog);
 +            for (i = 0; i < sys->natoms; i++)
 +            {
 +                clear_rvec(state->v[i]);
 +            }
 +            close_trj(fp);
 +            /* Search for a frame without velocities */
 +            bReadVel = FALSE;
 +            read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
 +        }
 +    }
 +
 +    state->natoms = fr.natoms;
 +
 +    if (sys->natoms != state->natoms)
 +    {
 +        gmx_fatal(FARGS, "Number of atoms in Topology "
 +                  "is not the same as in Trajectory");
 +    }
 +    copy_state(slog, &fr, bReadVel, state, &use_time);
 +
 +    /* Find the appropriate frame */
 +    while ((fr_time == -1 || fr.time < fr_time) &&
 +           read_next_frame(oenv, fp, &fr))
 +    {
 +        copy_state(slog, &fr, bReadVel, state, &use_time);
 +    }
 +
 +    close_trj(fp);
 +
 +    /* Set the relative box lengths for preserving the box shape.
 +     * Note that this call can lead to differences in the last bit
 +     * with respect to using gmx convert-tpr to create a [TT].tpx[tt] file.
 +     */
 +    set_box_rel(ir, state);
 +
 +    fprintf(stderr, "Using frame at t = %g ps\n", use_time);
 +    fprintf(stderr, "Starting time for run is %g ps\n", ir->init_t);
 +
 +    if ((ir->epc != epcNO  || ir->etc == etcNOSEHOOVER) && ener)
 +    {
 +        get_enx_state(ener, use_time, &sys->groups, ir, state);
 +        preserve_box_shape(ir, state->box_rel, state->boxv);
 +    }
 +}
 +
 +static void read_posres(gmx_mtop_t *mtop, t_molinfo *molinfo, gmx_bool bTopB,
 +                        char *fn,
 +                        int rc_scaling, int ePBC,
 +                        rvec com,
 +                        warninp_t wi)
 +{
 +    gmx_bool        bFirst = TRUE, *hadAtom;
 +    rvec           *x, *v, *xp;
 +    dvec            sum;
 +    double          totmass;
 +    t_atoms         dumat;
 +    matrix          box, invbox;
 +    int             natoms, npbcdim = 0;
 +    char            warn_buf[STRLEN], title[STRLEN];
 +    int             a, i, ai, j, k, mb, nat_molb;
 +    gmx_molblock_t *molb;
 +    t_params       *pr, *prfb;
 +    t_atom         *atom;
 +
 +    get_stx_coordnum(fn, &natoms);
 +    if (natoms != mtop->natoms)
 +    {
 +        sprintf(warn_buf, "The number of atoms in %s (%d) does not match the number of atoms in the topology (%d). Will assume that the first %d atoms in the topology and %s match.", fn, natoms, mtop->natoms, min(mtop->natoms, natoms), fn);
 +        warning(wi, warn_buf);
 +    }
 +    snew(x, natoms);
 +    snew(v, natoms);
 +    init_t_atoms(&dumat, natoms, FALSE);
 +    read_stx_conf(fn, title, &dumat, x, v, NULL, box);
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +    clear_rvec(com);
 +    if (rc_scaling != erscNO)
 +    {
 +        copy_mat(box, invbox);
 +        for (j = npbcdim; j < DIM; j++)
 +        {
 +            clear_rvec(invbox[j]);
 +            invbox[j][j] = 1;
 +        }
 +        m_inv_ur0(invbox, invbox);
 +    }
 +
 +    /* Copy the reference coordinates to mtop */
 +    clear_dvec(sum);
 +    totmass = 0;
 +    a       = 0;
 +    snew(hadAtom, natoms);
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb     = &mtop->molblock[mb];
 +        nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr;
 +        pr       = &(molinfo[molb->type].plist[F_POSRES]);
 +        prfb     = &(molinfo[molb->type].plist[F_FBPOSRES]);
 +        if (pr->nr > 0 || prfb->nr > 0)
 +        {
 +            atom = mtop->moltype[molb->type].atoms.atom;
 +            for (i = 0; (i < pr->nr); i++)
 +            {
 +                ai = pr->param[i].AI;
 +                if (ai >= natoms)
 +                {
 +                    gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n",
 +                              ai+1, *molinfo[molb->type].name, fn, natoms);
 +                }
 +                hadAtom[ai] = TRUE;
 +                if (rc_scaling == erscCOM)
 +                {
 +                    /* Determine the center of mass of the posres reference coordinates */
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        sum[j] += atom[ai].m*x[a+ai][j];
 +                    }
 +                    totmass  += atom[ai].m;
 +                }
 +            }
 +            /* Same for flat-bottomed posres, but do not count an atom twice for COM */
 +            for (i = 0; (i < prfb->nr); i++)
 +            {
 +                ai = prfb->param[i].AI;
 +                if (ai >= natoms)
 +                {
 +                    gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n",
 +                              ai+1, *molinfo[molb->type].name, fn, natoms);
 +                }
 +                if (rc_scaling == erscCOM && hadAtom[ai] == FALSE)
 +                {
 +                    /* Determine the center of mass of the posres reference coordinates */
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        sum[j] += atom[ai].m*x[a+ai][j];
 +                    }
 +                    totmass  += atom[ai].m;
 +                }
 +            }
 +            if (!bTopB)
 +            {
 +                molb->nposres_xA = nat_molb;
 +                snew(molb->posres_xA, molb->nposres_xA);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    copy_rvec(x[a+i], molb->posres_xA[i]);
 +                }
 +            }
 +            else
 +            {
 +                molb->nposres_xB = nat_molb;
 +                snew(molb->posres_xB, molb->nposres_xB);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    copy_rvec(x[a+i], molb->posres_xB[i]);
 +                }
 +            }
 +        }
 +        a += nat_molb;
 +    }
 +    if (rc_scaling == erscCOM)
 +    {
 +        if (totmass == 0)
 +        {
 +            gmx_fatal(FARGS, "The total mass of the position restraint atoms is 0");
 +        }
 +        for (j = 0; j < npbcdim; j++)
 +        {
 +            com[j] = sum[j]/totmass;
 +        }
 +        fprintf(stderr, "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f\n", com[XX], com[YY], com[ZZ]);
 +    }
 +
 +    if (rc_scaling != erscNO)
 +    {
 +        assert(npbcdim <= DIM);
 +
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            molb     = &mtop->molblock[mb];
 +            nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr;
 +            if (molb->nposres_xA > 0 || molb->nposres_xB > 0)
 +            {
 +                xp = (!bTopB ? molb->posres_xA : molb->posres_xB);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        if (rc_scaling == erscALL)
 +                        {
 +                            /* Convert from Cartesian to crystal coordinates */
 +                            xp[i][j] *= invbox[j][j];
 +                            for (k = j+1; k < npbcdim; k++)
 +                            {
 +                                xp[i][j] += invbox[k][j]*xp[i][k];
 +                            }
 +                        }
 +                        else if (rc_scaling == erscCOM)
 +                        {
 +                            /* Subtract the center of mass */
 +                            xp[i][j] -= com[j];
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (rc_scaling == erscCOM)
 +        {
 +            /* Convert the COM from Cartesian to crystal coordinates */
 +            for (j = 0; j < npbcdim; j++)
 +            {
 +                com[j] *= invbox[j][j];
 +                for (k = j+1; k < npbcdim; k++)
 +                {
 +                    com[j] += invbox[k][j]*com[k];
 +                }
 +            }
 +        }
 +    }
 +
 +    free_t_atoms(&dumat, TRUE);
 +    sfree(x);
 +    sfree(v);
 +    sfree(hadAtom);
 +}
 +
 +static void gen_posres(gmx_mtop_t *mtop, t_molinfo *mi,
 +                       char *fnA, char *fnB,
 +                       int rc_scaling, int ePBC,
 +                       rvec com, rvec comB,
 +                       warninp_t wi)
 +{
 +    int i, j;
 +
 +    read_posres  (mtop, mi, FALSE, fnA, rc_scaling, ePBC, com, wi);
 +    if (strcmp(fnA, fnB) != 0)
 +    {
 +        read_posres(mtop, mi, TRUE, fnB, rc_scaling, ePBC, comB, wi);
 +    }
 +}
 +
 +static void set_wall_atomtype(gpp_atomtype_t at, t_gromppopts *opts,
 +                              t_inputrec *ir, warninp_t wi)
 +{
 +    int  i;
 +    char warn_buf[STRLEN];
 +
 +    if (ir->nwall > 0)
 +    {
 +        fprintf(stderr, "Searching the wall atom type(s)\n");
 +    }
 +    for (i = 0; i < ir->nwall; i++)
 +    {
 +        ir->wall_atomtype[i] = get_atomtype_type(opts->wall_atomtype[i], at);
 +        if (ir->wall_atomtype[i] == NOTSET)
 +        {
 +            sprintf(warn_buf, "Specified wall atom type %s is not defined", opts->wall_atomtype[i]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static int nrdf_internal(t_atoms *atoms)
 +{
 +    int i, nmass, nrdf;
 +
 +    nmass = 0;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        /* Vsite ptype might not be set here yet, so also check the mass */
 +        if ((atoms->atom[i].ptype == eptAtom ||
 +             atoms->atom[i].ptype == eptNucleus)
 +            && atoms->atom[i].m > 0)
 +        {
 +            nmass++;
 +        }
 +    }
 +    switch (nmass)
 +    {
 +        case 0:  nrdf = 0; break;
 +        case 1:  nrdf = 0; break;
 +        case 2:  nrdf = 1; break;
 +        default: nrdf = nmass*3 - 6; break;
 +    }
 +
 +    return nrdf;
 +}
 +
 +void
 +spline1d( double        dx,
 +          double *      y,
 +          int           n,
 +          double *      u,
 +          double *      y2 )
 +{
 +    int    i;
 +    double p, q;
 +
 +    y2[0] = 0.0;
 +    u[0]  = 0.0;
 +
 +    for (i = 1; i < n-1; i++)
 +    {
 +        p     = 0.5*y2[i-1]+2.0;
 +        y2[i] = -0.5/p;
 +        q     = (y[i+1]-2.0*y[i]+y[i-1])/dx;
 +        u[i]  = (3.0*q/dx-0.5*u[i-1])/p;
 +    }
 +
 +    y2[n-1] = 0.0;
 +
 +    for (i = n-2; i >= 0; i--)
 +    {
 +        y2[i] = y2[i]*y2[i+1]+u[i];
 +    }
 +}
 +
 +
 +void
 +interpolate1d( double     xmin,
 +               double     dx,
 +               double *   ya,
 +               double *   y2a,
 +               double     x,
 +               double *   y,
 +               double *   y1)
 +{
 +    int    ix;
 +    double a, b;
 +
 +    ix = (x-xmin)/dx;
 +
 +    a = (xmin+(ix+1)*dx-x)/dx;
 +    b = (x-xmin-ix*dx)/dx;
 +
 +    *y  = a*ya[ix]+b*ya[ix+1]+((a*a*a-a)*y2a[ix]+(b*b*b-b)*y2a[ix+1])*(dx*dx)/6.0;
 +    *y1 = (ya[ix+1]-ya[ix])/dx-(3.0*a*a-1.0)/6.0*dx*y2a[ix]+(3.0*b*b-1.0)/6.0*dx*y2a[ix+1];
 +}
 +
 +
 +void
 +setup_cmap (int              grid_spacing,
 +            int              nc,
 +            real *           grid,
 +            gmx_cmap_t *     cmap_grid)
 +{
 +    double *tmp_u, *tmp_u2, *tmp_yy, *tmp_y1, *tmp_t2, *tmp_grid;
 +
 +    int     i, j, k, ii, jj, kk, idx;
 +    int     offset;
 +    double  dx, xmin, v, v1, v2, v12;
 +    double  phi, psi;
 +
 +    snew(tmp_u, 2*grid_spacing);
 +    snew(tmp_u2, 2*grid_spacing);
 +    snew(tmp_yy, 2*grid_spacing);
 +    snew(tmp_y1, 2*grid_spacing);
 +    snew(tmp_t2, 2*grid_spacing*2*grid_spacing);
 +    snew(tmp_grid, 2*grid_spacing*2*grid_spacing);
 +
 +    dx   = 360.0/grid_spacing;
 +    xmin = -180.0-dx*grid_spacing/2;
 +
 +    for (kk = 0; kk < nc; kk++)
 +    {
 +        /* Compute an offset depending on which cmap we are using
 +         * Offset will be the map number multiplied with the
 +         * grid_spacing * grid_spacing * 2
 +         */
 +        offset = kk * grid_spacing * grid_spacing * 2;
 +
 +        for (i = 0; i < 2*grid_spacing; i++)
 +        {
 +            ii = (i+grid_spacing-grid_spacing/2)%grid_spacing;
 +
 +            for (j = 0; j < 2*grid_spacing; j++)
 +            {
 +                jj = (j+grid_spacing-grid_spacing/2)%grid_spacing;
 +                tmp_grid[i*grid_spacing*2+j] = grid[offset+ii*grid_spacing+jj];
 +            }
 +        }
 +
 +        for (i = 0; i < 2*grid_spacing; i++)
 +        {
 +            spline1d(dx, &(tmp_grid[2*grid_spacing*i]), 2*grid_spacing, tmp_u, &(tmp_t2[2*grid_spacing*i]));
 +        }
 +
 +        for (i = grid_spacing/2; i < grid_spacing+grid_spacing/2; i++)
 +        {
 +            ii  = i-grid_spacing/2;
 +            phi = ii*dx-180.0;
 +
 +            for (j = grid_spacing/2; j < grid_spacing+grid_spacing/2; j++)
 +            {
 +                jj  = j-grid_spacing/2;
 +                psi = jj*dx-180.0;
 +
 +                for (k = 0; k < 2*grid_spacing; k++)
 +                {
 +                    interpolate1d(xmin, dx, &(tmp_grid[2*grid_spacing*k]),
 +                                  &(tmp_t2[2*grid_spacing*k]), psi, &tmp_yy[k], &tmp_y1[k]);
 +                }
 +
 +                spline1d(dx, tmp_yy, 2*grid_spacing, tmp_u, tmp_u2);
 +                interpolate1d(xmin, dx, tmp_yy, tmp_u2, phi, &v, &v1);
 +                spline1d(dx, tmp_y1, 2*grid_spacing, tmp_u, tmp_u2);
 +                interpolate1d(xmin, dx, tmp_y1, tmp_u2, phi, &v2, &v12);
 +
 +                idx = ii*grid_spacing+jj;
 +                cmap_grid->cmapdata[kk].cmap[idx*4]   = grid[offset+ii*grid_spacing+jj];
 +                cmap_grid->cmapdata[kk].cmap[idx*4+1] = v1;
 +                cmap_grid->cmapdata[kk].cmap[idx*4+2] = v2;
 +                cmap_grid->cmapdata[kk].cmap[idx*4+3] = v12;
 +            }
 +        }
 +    }
 +}
 +
 +void init_cmap_grid(gmx_cmap_t *cmap_grid, int ngrid, int grid_spacing)
 +{
 +    int i, k, nelem;
 +
 +    cmap_grid->ngrid        = ngrid;
 +    cmap_grid->grid_spacing = grid_spacing;
 +    nelem                   = cmap_grid->grid_spacing*cmap_grid->grid_spacing;
 +
 +    snew(cmap_grid->cmapdata, ngrid);
 +
 +    for (i = 0; i < cmap_grid->ngrid; i++)
 +    {
 +        snew(cmap_grid->cmapdata[i].cmap, 4*nelem);
 +    }
 +}
 +
 +
 +static int count_constraints(gmx_mtop_t *mtop, t_molinfo *mi, warninp_t wi)
 +{
 +    int             count, count_mol, i, mb;
 +    gmx_molblock_t *molb;
 +    t_params       *plist;
 +    char            buf[STRLEN];
 +
 +    count = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        count_mol = 0;
 +        molb      = &mtop->molblock[mb];
 +        plist     = mi[molb->type].plist;
 +
 +        for (i = 0; i < F_NRE; i++)
 +        {
 +            if (i == F_SETTLE)
 +            {
 +                count_mol += 3*plist[i].nr;
 +            }
 +            else if (interaction_function[i].flags & IF_CONSTRAINT)
 +            {
 +                count_mol += plist[i].nr;
 +            }
 +        }
 +
 +        if (count_mol > nrdf_internal(&mi[molb->type].atoms))
 +        {
 +            sprintf(buf,
 +                    "Molecule type '%s' has %d constraints.\n"
 +                    "For stability and efficiency there should not be more constraints than internal number of degrees of freedom: %d.\n",
 +                    *mi[molb->type].name, count_mol,
 +                    nrdf_internal(&mi[molb->type].atoms));
 +            warning(wi, buf);
 +        }
 +        count += molb->nmol*count_mol;
 +    }
 +
 +    return count;
 +}
 +
 +static void check_gbsa_params_charged(gmx_mtop_t *sys, gpp_atomtype_t atype)
 +{
 +    int            i, nmiss, natoms, mt;
 +    real           q;
 +    const t_atoms *atoms;
 +
 +    nmiss = 0;
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        atoms  = &sys->moltype[mt].atoms;
 +        natoms = atoms->nr;
 +
 +        for (i = 0; i < natoms; i++)
 +        {
 +            q = atoms->atom[i].q;
 +            if ((get_atomtype_radius(atoms->atom[i].type, atype)    == 0  ||
 +                 get_atomtype_vol(atoms->atom[i].type, atype)       == 0  ||
 +                 get_atomtype_surftens(atoms->atom[i].type, atype)  == 0  ||
 +                 get_atomtype_gb_radius(atoms->atom[i].type, atype) == 0  ||
 +                 get_atomtype_S_hct(atoms->atom[i].type, atype)     == 0) &&
 +                q != 0)
 +            {
 +                fprintf(stderr, "\nGB parameter(s) zero for atom type '%s' while charge is %g\n",
 +                        get_atomtype_name(atoms->atom[i].type, atype), q);
 +                nmiss++;
 +            }
 +        }
 +    }
 +
 +    if (nmiss > 0)
 +    {
 +        gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield has parameters with value zero for %d atomtypes that occur as charged atoms.", nmiss);
 +    }
 +}
 +
 +
 +static void check_gbsa_params(gpp_atomtype_t atype)
 +{
 +    int  nmiss, i;
 +
 +    /* If we are doing GBSA, check that we got the parameters we need
 +     * This checking is to see if there are GBSA paratmeters for all
 +     * atoms in the force field. To go around this for testing purposes
 +     * comment out the nerror++ counter temporarily
 +     */
 +    nmiss = 0;
 +    for (i = 0; i < get_atomtype_ntypes(atype); i++)
 +    {
 +        if (get_atomtype_radius(i, atype)    < 0 ||
 +            get_atomtype_vol(i, atype)       < 0 ||
 +            get_atomtype_surftens(i, atype)  < 0 ||
 +            get_atomtype_gb_radius(i, atype) < 0 ||
 +            get_atomtype_S_hct(i, atype)     < 0)
 +        {
 +            fprintf(stderr, "\nGB parameter(s) missing or negative for atom type '%s'\n",
 +                    get_atomtype_name(i, atype));
 +            nmiss++;
 +        }
 +    }
 +
 +    if (nmiss > 0)
 +    {
 +        gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield is missing parameters for %d atomtypes or they might be negative.", nmiss);
 +    }
 +
 +}
 +
 +static real calc_temp(const gmx_mtop_t *mtop,
 +                      const t_inputrec *ir,
 +                      rvec             *v)
 +{
 +    double                  sum_mv2;
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     a;
 +    int                     nrdf, g;
 +
 +    sum_mv2 = 0;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        sum_mv2 += atom->m*norm2(v[a]);
 +    }
 +
 +    nrdf = 0;
 +    for (g = 0; g < ir->opts.ngtc; g++)
 +    {
 +        nrdf += ir->opts.nrdf[g];
 +    }
 +
 +    return sum_mv2/(nrdf*BOLTZ);
 +}
 +
 +static real get_max_reference_temp(const t_inputrec *ir,
 +                                   warninp_t         wi)
 +{
 +    real     ref_t;
 +    int      i;
 +    gmx_bool bNoCoupl;
 +
 +    ref_t    = 0;
 +    bNoCoupl = FALSE;
 +    for (i = 0; i < ir->opts.ngtc; i++)
 +    {
 +        if (ir->opts.tau_t[i] < 0)
 +        {
 +            bNoCoupl = TRUE;
 +        }
 +        else
 +        {
 +            ref_t = max(ref_t, ir->opts.ref_t[i]);
 +        }
 +    }
 +
 +    if (bNoCoupl)
 +    {
 +        char buf[STRLEN];
 +
 +        sprintf(buf, "Some temperature coupling groups do not use temperature coupling. We will assume their temperature is not more than %.3f K. If their temperature is higher, the energy error and the Verlet buffer might be underestimated.",
 +                ref_t);
 +        warning(wi, buf);
 +    }
 +
 +    return ref_t;
 +}
 +
 +static void set_verlet_buffer(const gmx_mtop_t *mtop,
 +                              t_inputrec       *ir,
 +                              real              buffer_temp,
 +                              matrix            box,
 +                              warninp_t         wi)
 +{
 +    int                    i;
 +    verletbuf_list_setup_t ls;
 +    real                   rlist_1x1;
 +    int                    n_nonlin_vsite;
 +    char                   warn_buf[STRLEN];
 +
 +    printf("Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K\n", ir->verletbuf_tol, buffer_temp);
 +
 +    /* Calculate the buffer size for simple atom vs atoms list */
 +    ls.cluster_size_i = 1;
 +    ls.cluster_size_j = 1;
 +    calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp,
 +                            &ls, &n_nonlin_vsite, &rlist_1x1);
 +
 +    /* Set the pair-list buffer size in ir */
 +    verletbuf_get_list_setup(FALSE, &ls);
 +    calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp,
 +                            &ls, &n_nonlin_vsite, &ir->rlist);
 +
 +    if (n_nonlin_vsite > 0)
 +    {
 +        sprintf(warn_buf, "There are %d non-linear virtual site constructions. Their contribution to the energy error is approximated. In most cases this does not affect the error significantly.", n_nonlin_vsite);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    printf("Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm\n",
 +           1, 1, rlist_1x1, rlist_1x1-max(ir->rvdw, ir->rcoulomb));
 +
 +    ir->rlistlong = ir->rlist;
 +    printf("Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm\n",
 +           ls.cluster_size_i, ls.cluster_size_j,
 +           ir->rlist, ir->rlist-max(ir->rvdw, ir->rcoulomb));
 +
 +    if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box))
 +    {
 +        gmx_fatal(FARGS, "The pair-list cut-off (%g nm) is longer than half the shortest box vector or longer than the smallest box diagonal element (%g nm). Increase the box size or decrease nstlist or increase verlet-buffer-tolerance.", ir->rlistlong, sqrt(max_cutoff2(ir->ePBC, box)));
 +    }
 +}
 +
 +int gmx_grompp(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[THISMODULE] (the gromacs preprocessor)",
 +        "reads a molecular topology file, checks the validity of the",
 +        "file, expands the topology from a molecular description to an atomic",
 +        "description. The topology file contains information about",
 +        "molecule types and the number of molecules, the preprocessor",
 +        "copies each molecule as needed. ",
 +        "There is no limitation on the number of molecule types. ",
 +        "Bonds and bond-angles can be converted into constraints, separately",
 +        "for hydrogens and heavy atoms.",
 +        "Then a coordinate file is read and velocities can be generated",
 +        "from a Maxwellian distribution if requested.",
 +        "[THISMODULE] also reads parameters for [gmx-mdrun] ",
 +        "(eg. number of MD steps, time step, cut-off), and others such as",
 +        "NEMD parameters, which are corrected so that the net acceleration",
 +        "is zero.",
 +        "Eventually a binary file is produced that can serve as the sole input",
 +        "file for the MD program.[PAR]",
 +
 +        "[THISMODULE] uses the atom names from the topology file. The atom names",
 +        "in the coordinate file (option [TT]-c[tt]) are only read to generate",
 +        "warnings when they do not match the atom names in the topology.",
 +        "Note that the atom names are irrelevant for the simulation as",
 +        "only the atom types are used for generating interaction parameters.[PAR]",
 +
 +        "[THISMODULE] uses a built-in preprocessor to resolve includes, macros, ",
 +        "etc. The preprocessor supports the following keywords:[PAR]",
 +        "#ifdef VARIABLE[BR]",
 +        "#ifndef VARIABLE[BR]",
 +        "#else[BR]",
 +        "#endif[BR]",
 +        "#define VARIABLE[BR]",
 +        "#undef VARIABLE[BR]"
 +        "#include \"filename\"[BR]",
 +        "#include <filename>[PAR]",
 +        "The functioning of these statements in your topology may be modulated by",
 +        "using the following two flags in your [TT].mdp[tt] file:[PAR]",
 +        "[TT]define = -DVARIABLE1 -DVARIABLE2[BR]",
 +        "include = -I/home/john/doe[tt][BR]",
 +        "For further information a C-programming textbook may help you out.",
 +        "Specifying the [TT]-pp[tt] flag will get the pre-processed",
 +        "topology file written out so that you can verify its contents.[PAR]",
 +
 +        /* cpp has been unnecessary for some time, hasn't it?
 +            "If your system does not have a C-preprocessor, you can still",
 +            "use [TT]grompp[tt], but you do not have access to the features ",
 +            "from the cpp. Command line options to the C-preprocessor can be given",
 +            "in the [TT].mdp[tt] file. See your local manual (man cpp).[PAR]",
 +         */
 +
 +        "When using position restraints a file with restraint coordinates",
 +        "can be supplied with [TT]-r[tt], otherwise restraining will be done",
 +        "with respect to the conformation from the [TT]-c[tt] option.",
 +        "For free energy calculation the the coordinates for the B topology",
 +        "can be supplied with [TT]-rb[tt], otherwise they will be equal to",
 +        "those of the A topology.[PAR]",
 +
 +        "Starting coordinates can be read from trajectory with [TT]-t[tt].",
 +        "The last frame with coordinates and velocities will be read,",
 +        "unless the [TT]-time[tt] option is used. Only if this information",
 +        "is absent will the coordinates in the [TT]-c[tt] file be used.",
 +        "Note that these velocities will not be used when [TT]gen_vel = yes[tt]",
 +        "in your [TT].mdp[tt] file. An energy file can be supplied with",
 +        "[TT]-e[tt] to read Nose-Hoover and/or Parrinello-Rahman coupling",
 +        "variables.[PAR]",
 +
 +        "[THISMODULE] can be used to restart simulations (preserving",
 +        "continuity) by supplying just a checkpoint file with [TT]-t[tt].",
 +        "However, for simply changing the number of run steps to extend",
 +        "a run, using [gmx-convert-tpr] is more convenient than [THISMODULE].",
 +        "You then supply the old checkpoint file directly to [gmx-mdrun]",
 +        "with [TT]-cpi[tt]. If you wish to change the ensemble or things",
 +        "like output frequency, then supplying the checkpoint file to",
 +        "[THISMODULE] with [TT]-t[tt] along with a new [TT].mdp[tt] file",
 +        "with [TT]-f[tt] is the recommended procedure.[PAR]",
 +
 +        "By default, all bonded interactions which have constant energy due to",
 +        "virtual site constructions will be removed. If this constant energy is",
 +        "not zero, this will result in a shift in the total energy. All bonded",
 +        "interactions can be kept by turning off [TT]-rmvsbds[tt]. Additionally,",
 +        "all constraints for distances which will be constant anyway because",
 +        "of virtual site constructions will be removed. If any constraints remain",
 +        "which involve virtual sites, a fatal error will result.[PAR]"
 +
 +        "To verify your run input file, please take note of all warnings",
 +        "on the screen, and correct where necessary. Do also look at the contents",
 +        "of the [TT]mdout.mdp[tt] file; this contains comment lines, as well as",
 +        "the input that [THISMODULE] has read. If in doubt, you can start [THISMODULE]",
 +        "with the [TT]-debug[tt] option which will give you more information",
 +        "in a file called [TT]grompp.log[tt] (along with real debug info). You",
 +        "can see the contents of the run input file with the [gmx-dump]",
 +        "program. [gmx-check] can be used to compare the contents of two",
 +        "run input files.[PAR]"
 +
 +        "The [TT]-maxwarn[tt] option can be used to override warnings printed",
 +        "by [THISMODULE] that otherwise halt output. In some cases, warnings are",
 +        "harmless, but usually they are not. The user is advised to carefully",
 +        "interpret the output messages before attempting to bypass them with",
 +        "this option."
 +    };
 +    t_gromppopts      *opts;
 +    gmx_mtop_t        *sys;
 +    int                nmi;
 +    t_molinfo         *mi;
 +    gpp_atomtype_t     atype;
 +    t_inputrec        *ir;
 +    int                natoms, nvsite, comb, mt;
 +    t_params          *plist;
 +    t_state            state;
 +    matrix             box;
 +    real               max_spacing, fudgeQQ;
 +    double             reppow;
 +    char               fn[STRLEN], fnB[STRLEN];
 +    const char        *mdparin;
 +    int                ntype;
 +    gmx_bool           bNeedVel, bGenVel;
 +    gmx_bool           have_atomnumber;
 +    int                n12, n13, n14;
 +    t_params          *gb_plist = NULL;
 +    gmx_genborn_t     *born     = NULL;
 +    output_env_t       oenv;
 +    gmx_bool           bVerbose = FALSE;
 +    warninp_t          wi;
 +    char               warn_buf[STRLEN];
 +    unsigned int       useed;
 +    t_atoms            IMDatoms;   /* Atoms to be operated on interactively (IMD) */
 +
 +    t_filenm           fnm[] = {
 +        { efMDP, NULL,  NULL,        ffREAD  },
 +        { efMDP, "-po", "mdout",     ffWRITE },
 +        { efSTX, "-c",  NULL,        ffREAD  },
 +        { efSTX, "-r",  NULL,        ffOPTRD },
 +        { efSTX, "-rb", NULL,        ffOPTRD },
 +        { efNDX, NULL,  NULL,        ffOPTRD },
 +        { efTOP, NULL,  NULL,        ffREAD  },
 +        { efTOP, "-pp", "processed", ffOPTWR },
 +        { efTPX, "-o",  NULL,        ffWRITE },
 +        { efTRN, "-t",  NULL,        ffOPTRD },
 +        { efEDR, "-e",  NULL,        ffOPTRD },
 +        /* This group is needed by the VMD viewer as the start configuration for IMD sessions: */
 +        { efGRO, "-imd", "imdgroup", ffOPTWR },
 +        { efTRN, "-ref", "rotref",   ffOPTRW }
 +    };
 +#define NFILE asize(fnm)
 +
 +    /* Command line options */
 +    static gmx_bool bRenum   = TRUE;
 +    static gmx_bool bRmVSBds = TRUE, bZero = FALSE;
 +    static int      i, maxwarn = 0;
 +    static real     fr_time = -1;
 +    t_pargs         pa[]    = {
 +        { "-v",       FALSE, etBOOL, {&bVerbose},
 +          "Be loud and noisy" },
 +        { "-time",    FALSE, etREAL, {&fr_time},
 +          "Take frame at or first after this time." },
 +        { "-rmvsbds", FALSE, etBOOL, {&bRmVSBds},
 +          "Remove constant bonded interactions with virtual sites" },
 +        { "-maxwarn", FALSE, etINT,  {&maxwarn},
 +          "Number of allowed warnings during input processing. Not for normal use and may generate unstable systems" },
 +        { "-zero",    FALSE, etBOOL, {&bZero},
 +          "Set parameters for bonded interactions without defaults to zero instead of generating an error" },
 +        { "-renum",   FALSE, etBOOL, {&bRenum},
 +          "Renumber atomtypes and minimize number of atomtypes" }
 +    };
 +
 +    /* Initiate some variables */
 +    snew(ir, 1);
 +    snew(opts, 1);
 +    init_ir(ir, opts);
 +
 +    /* Parse the command line */
 +    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
 +                           asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    wi = init_warning(TRUE, maxwarn);
 +
 +    /* PARAMETER file processing */
 +    mdparin = opt2fn("-f", NFILE, fnm);
 +    set_warning_line(wi, mdparin, -1);
 +    get_ir(mdparin, opt2fn("-po", NFILE, fnm), ir, opts, wi);
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "checking input for internal consistency...\n");
 +    }
 +    check_ir(mdparin, ir, opts, wi);
 +
 +    if (ir->ld_seed == -1)
 +    {
 +        ir->ld_seed = (gmx_int64_t)gmx_rng_make_seed();
 +        fprintf(stderr, "Setting the LD random seed to %"GMX_PRId64 "\n", ir->ld_seed);
 +    }
 +
 +    if (ir->expandedvals->lmc_seed == -1)
 +    {
 +        ir->expandedvals->lmc_seed = (int)gmx_rng_make_seed();
 +        fprintf(stderr, "Setting the lambda MC random seed to %d\n", ir->expandedvals->lmc_seed);
 +    }
 +
 +    bNeedVel = EI_STATE_VELOCITY(ir->eI);
 +    bGenVel  = (bNeedVel && opts->bGenVel);
 +    if (bGenVel && ir->bContinuation)
 +    {
 +        sprintf(warn_buf,
 +                "Generating velocities is inconsistent with attempting "
 +                "to continue a previous run. Choose only one of "
 +                "gen-vel = yes and continuation = yes.");
 +        warning_error(wi, warn_buf);
 +    }
 +
 +    snew(plist, F_NRE);
 +    init_plist(plist);
 +    snew(sys, 1);
 +    atype = init_atomtype();
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "Just opened", &sys->symtab);
 +    }
 +
 +    strcpy(fn, ftp2fn(efTOP, NFILE, fnm));
 +    if (!gmx_fexist(fn))
 +    {
 +        gmx_fatal(FARGS, "%s does not exist", fn);
 +    }
 +    new_status(fn, opt2fn_null("-pp", NFILE, fnm), opt2fn("-c", NFILE, fnm),
 +               opts, ir, bZero, bGenVel, bVerbose, &state,
 +               atype, sys, &nmi, &mi, plist, &comb, &reppow, &fudgeQQ,
 +               opts->bMorse,
 +               wi);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After new_status", &sys->symtab);
 +    }
 +
 +    nvsite = 0;
 +    /* set parameters for virtual site construction (not for vsiten) */
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        nvsite +=
 +            set_vsites(bVerbose, &sys->moltype[mt].atoms, atype, mi[mt].plist);
 +    }
 +    /* now throw away all obsolete bonds, angles and dihedrals: */
 +    /* note: constraints are ALWAYS removed */
 +    if (nvsite)
 +    {
 +        for (mt = 0; mt < sys->nmoltype; mt++)
 +        {
 +            clean_vsite_bondeds(mi[mt].plist, sys->moltype[mt].atoms.nr, bRmVSBds);
 +        }
 +    }
 +
++    if (nvsite && ir->eI == eiNM)
++    {
++        gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
++    }
++
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        fprintf(stderr, "Removing all charge groups because cutoff-scheme=%s\n",
 +                ecutscheme_names[ir->cutoff_scheme]);
 +
 +        /* Remove all charge groups */
 +        gmx_mtop_remove_chargegroups(sys);
 +    }
 +
 +    if (count_constraints(sys, mi, wi) && (ir->eConstrAlg == econtSHAKE))
 +    {
 +        if (ir->eI == eiCG || ir->eI == eiLBFGS)
 +        {
 +            sprintf(warn_buf, "Can not do %s with %s, use %s",
 +                    EI(ir->eI), econstr_names[econtSHAKE], econstr_names[econtLINCS]);
 +            warning_error(wi, warn_buf);
 +        }
 +        if (ir->bPeriodicMols)
 +        {
 +            sprintf(warn_buf, "Can not do periodic molecules with %s, use %s",
 +                    econstr_names[econtSHAKE], econstr_names[econtLINCS]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EI_SD (ir->eI) &&  ir->etc != etcNO)
 +    {
 +        warning_note(wi, "Temperature coupling is ignored with SD integrators.");
 +    }
 +
 +    /* If we are doing QM/MM, check that we got the atom numbers */
 +    have_atomnumber = TRUE;
 +    for (i = 0; i < get_atomtype_ntypes(atype); i++)
 +    {
 +        have_atomnumber = have_atomnumber && (get_atomtype_atomnumber(i, atype) >= 0);
 +    }
 +    if (!have_atomnumber && ir->bQMMM)
 +    {
 +        warning_error(wi,
 +                      "\n"
 +                      "It appears as if you are trying to run a QM/MM calculation, but the force\n"
 +                      "field you are using does not contain atom numbers fields. This is an\n"
 +                      "optional field (introduced in Gromacs 3.3) for general runs, but mandatory\n"
 +                      "for QM/MM. The good news is that it is easy to add - put the atom number as\n"
 +                      "an integer just before the mass column in ffXXXnb.itp.\n"
 +                      "NB: United atoms have the same atom numbers as normal ones.\n\n");
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        if ((ir->adress->const_wf > 1) || (ir->adress->const_wf < 0))
 +        {
 +            warning_error(wi, "AdResS contant weighting function should be between 0 and 1\n\n");
 +        }
 +        /** TODO check size of ex+hy width against box size */
 +    }
 +
 +    /* Check for errors in the input now, since they might cause problems
 +     * during processing further down.
 +     */
 +    check_warning_error(wi, FARGS);
 +
 +    if (opt2bSet("-r", NFILE, fnm))
 +    {
 +        sprintf(fn, "%s", opt2fn("-r", NFILE, fnm));
 +    }
 +    else
 +    {
 +        sprintf(fn, "%s", opt2fn("-c", NFILE, fnm));
 +    }
 +    if (opt2bSet("-rb", NFILE, fnm))
 +    {
 +        sprintf(fnB, "%s", opt2fn("-rb", NFILE, fnm));
 +    }
 +    else
 +    {
 +        strcpy(fnB, fn);
 +    }
 +
 +    if (nint_ftype(sys, mi, F_POSRES) > 0 || nint_ftype(sys, mi, F_FBPOSRES) > 0)
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "Reading position restraint coords from %s", fn);
 +            if (strcmp(fn, fnB) == 0)
 +            {
 +                fprintf(stderr, "\n");
 +            }
 +            else
 +            {
 +                fprintf(stderr, " and %s\n", fnB);
 +            }
 +        }
 +        gen_posres(sys, mi, fn, fnB,
 +                   ir->refcoord_scaling, ir->ePBC,
 +                   ir->posres_com, ir->posres_comB,
 +                   wi);
 +    }
 +
 +    /* If we are using CMAP, setup the pre-interpolation grid */
 +    if (plist->ncmap > 0)
 +    {
 +        init_cmap_grid(&sys->ffparams.cmap_grid, plist->nc, plist->grid_spacing);
 +        setup_cmap(plist->grid_spacing, plist->nc, plist->cmap, &sys->ffparams.cmap_grid);
 +    }
 +
 +    set_wall_atomtype(atype, opts, ir, wi);
 +    if (bRenum)
 +    {
 +        renum_atype(plist, sys, ir->wall_atomtype, atype, bVerbose);
 +        ntype = get_atomtype_ntypes(atype);
 +    }
 +
 +    if (ir->implicit_solvent != eisNO)
 +    {
 +        /* Now we have renumbered the atom types, we can check the GBSA params */
 +        check_gbsa_params(atype);
 +
 +        /* Check that all atoms that have charge and/or LJ-parameters also have
 +         * sensible GB-parameters
 +         */
 +        check_gbsa_params_charged(sys, atype);
 +    }
 +
 +    /* PELA: Copy the atomtype data to the topology atomtype list */
 +    copy_atomtype_atomtypes(atype, &(sys->atomtypes));
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After renum_atype", &sys->symtab);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "converting bonded parameters...\n");
 +    }
 +
 +    ntype = get_atomtype_ntypes(atype);
 +    convert_params(ntype, plist, mi, comb, reppow, fudgeQQ, sys);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After convert_params", &sys->symtab);
 +    }
 +
 +    /* set ptype to VSite for virtual sites */
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        set_vsites_ptype(FALSE, &sys->moltype[mt]);
 +    }
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After virtual sites", &sys->symtab);
 +    }
 +    /* Check velocity for virtual sites and shells */
 +    if (bGenVel)
 +    {
 +        check_vel(sys, state.v);
 +    }
 +
 +    /* check masses */
 +    check_mol(sys, wi);
 +
 +    for (i = 0; i < sys->nmoltype; i++)
 +    {
 +        check_cg_sizes(ftp2fn(efTOP, NFILE, fnm), &sys->moltype[i].cgs, wi);
 +    }
 +
 +    if (EI_DYNAMICS(ir->eI) && ir->eI != eiBD)
 +    {
 +        check_bonds_timestep(sys, ir->delta_t, wi);
 +    }
 +
 +    if (EI_ENERGY_MINIMIZATION(ir->eI) && 0 == ir->nsteps)
 +    {
 +        warning_note(wi, "Zero-step energy minimization will alter the coordinates before calculating the energy. If you just want the energy of a single point, try zero-step MD (with unconstrained_start = yes). To do multiple single-point energy evaluations of different configurations of the same topology, use mdrun -rerun.");
 +    }
 +
 +    check_warning_error(wi, FARGS);
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "initialising group options...\n");
 +    }
 +    do_index(mdparin, ftp2fn_null(efNDX, NFILE, fnm),
 +             sys, bVerbose, ir,
 +             bGenVel ? state.v : NULL,
 +             wi);
 +
 +    if (ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0 &&
 +        ir->nstlist > 1)
 +    {
 +        if (EI_DYNAMICS(ir->eI) && inputrec2nboundeddim(ir) == 3)
 +        {
 +            real buffer_temp;
 +
 +            if (EI_MD(ir->eI) && ir->etc == etcNO)
 +            {
 +                if (bGenVel)
 +                {
 +                    buffer_temp = opts->tempi;
 +                }
 +                else
 +                {
 +                    buffer_temp = calc_temp(sys, ir, state.v);
 +                }
 +                if (buffer_temp > 0)
 +                {
 +                    sprintf(warn_buf, "NVE simulation: will use the initial temperature of %.3f K for determining the Verlet buffer size", buffer_temp);
 +                    warning_note(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    sprintf(warn_buf, "NVE simulation with an initial temperature of zero: will use a Verlet buffer of %d%%. Check your energy drift!",
 +                            (int)(verlet_buffer_ratio_NVE_T0*100 + 0.5));
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +            else
 +            {
 +                buffer_temp = get_max_reference_temp(ir, wi);
 +            }
 +
 +            if (EI_MD(ir->eI) && ir->etc == etcNO && buffer_temp == 0)
 +            {
 +                /* NVE with initial T=0: we add a fixed ratio to rlist.
 +                 * Since we don't actually use verletbuf_tol, we set it to -1
 +                 * so it can't be misused later.
 +                 */
 +                ir->rlist         *= 1.0 + verlet_buffer_ratio_NVE_T0;
 +                ir->verletbuf_tol  = -1;
 +            }
 +            else
 +            {
 +                /* We warn for NVE simulations with >1(.1)% drift tolerance */
 +                const real drift_tol = 0.01;
 +                real       ener_runtime;
 +
 +                /* We use 2 DOF per atom = 2kT pot+kin energy, to be on
 +                 * the safe side with constraints (without constraints: 3 DOF).
 +                 */
 +                ener_runtime = 2*BOLTZ*buffer_temp/(ir->nsteps*ir->delta_t);
 +
 +                if (EI_MD(ir->eI) && ir->etc == etcNO && ir->nstlist > 1 &&
 +                    ir->nsteps > 0 &&
 +                    ir->verletbuf_tol > 1.1*drift_tol*ener_runtime)
 +                {
 +                    sprintf(warn_buf, "You are using a Verlet buffer tolerance of %g kJ/mol/ps for an NVE simulation of length %g ps, which can give a final drift of %d%%. For conserving energy to %d%%, you might need to set verlet-buffer-tolerance to %.1e.",
 +                            ir->verletbuf_tol, ir->nsteps*ir->delta_t,
 +                            (int)(ir->verletbuf_tol/ener_runtime*100 + 0.5),
 +                            (int)(100*drift_tol + 0.5),
 +                            drift_tol*ener_runtime);
 +                    warning_note(wi, warn_buf);
 +                }
 +
 +                set_verlet_buffer(sys, ir, buffer_temp, state.box, wi);
 +            }
 +        }
 +    }
 +
 +    /* Init the temperature coupling state */
 +    init_gtc_state(&state, ir->opts.ngtc, 0, ir->opts.nhchainlength); /* need to add nnhpres here? */
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Checking consistency between energy and charge groups...\n");
 +    }
 +    check_eg_vs_cg(sys);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After index", &sys->symtab);
 +    }
 +
 +    triple_check(mdparin, ir, sys, wi);
 +    close_symtab(&sys->symtab);
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After close", &sys->symtab);
 +    }
 +
 +    /* make exclusions between QM atoms */
 +    if (ir->bQMMM)
 +    {
 +        if (ir->QMMMscheme == eQMMMschemenormal && ir->ns_type == ensSIMPLE)
 +        {
 +            gmx_fatal(FARGS, "electrostatic embedding only works with grid neighboursearching, use ns-type=grid instead\n");
 +        }
 +        else
 +        {
 +            generate_qmexcl(sys, ir, wi);
 +        }
 +    }
 +
 +    if (ftp2bSet(efTRN, NFILE, fnm))
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "getting data from old trajectory ...\n");
 +        }
 +        cont_status(ftp2fn(efTRN, NFILE, fnm), ftp2fn_null(efEDR, NFILE, fnm),
 +                    bNeedVel, bGenVel, fr_time, ir, &state, sys, oenv);
 +    }
 +
 +    if (ir->ePBC == epbcXY && ir->nwall != 2)
 +    {
 +        clear_rvec(state.box[ZZ]);
 +    }
 +
 +    if (ir->cutoff_scheme != ecutsVERLET && ir->rlist > 0)
 +    {
 +        set_warning_line(wi, mdparin, -1);
 +        check_chargegroup_radii(sys, ir, state.x, wi);
 +    }
 +
 +    if (EEL_FULL(ir->coulombtype) || EVDW_PME(ir->vdwtype))
 +    {
 +        /* Calculate the optimal grid dimensions */
 +        copy_mat(state.box, box);
 +        if (ir->ePBC == epbcXY && ir->nwall == 2)
 +        {
 +            svmul(ir->wall_ewald_zfac, box[ZZ], box[ZZ]);
 +        }
 +        if (ir->nkx > 0 && ir->nky > 0 && ir->nkz > 0)
 +        {
 +            /* Mark fourier_spacing as not used */
 +            ir->fourier_spacing = 0;
 +        }
 +        else if (ir->nkx != 0 && ir->nky != 0 && ir->nkz != 0)
 +        {
 +            set_warning_line(wi, mdparin, -1);
 +            warning_error(wi, "Some of the Fourier grid sizes are set, but all of them need to be set.");
 +        }
 +        max_spacing = calc_grid(stdout, box, ir->fourier_spacing,
 +                                &(ir->nkx), &(ir->nky), &(ir->nkz));
 +    }
 +
 +    /* MRS: eventually figure out better logic for initializing the fep
 +       values that makes declaring the lambda and declaring the state not
 +       potentially conflict if not handled correctly. */
 +    if (ir->efep != efepNO)
 +    {
 +        state.fep_state = ir->fepvals->init_fep_state;
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            /* init_lambda trumps state definitions*/
 +            if (ir->fepvals->init_lambda >= 0)
 +            {
 +                state.lambda[i] = ir->fepvals->init_lambda;
 +            }
 +            else
 +            {
 +                if (ir->fepvals->all_lambda[i] == NULL)
 +                {
 +                    gmx_fatal(FARGS, "Values of lambda not set for a free energy calculation!");
 +                }
 +                else
 +                {
 +                    state.lambda[i] = ir->fepvals->all_lambda[i][state.fep_state];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        set_pull_init(ir, sys, state.x, state.box, state.lambda[efptMASS], oenv, opts->pull_start);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        set_reference_positions(ir->rot, state.x, state.box,
 +                                opt2fn("-ref", NFILE, fnm), opt2bSet("-ref", NFILE, fnm),
 +                                wi);
 +    }
 +
 +    /*  reset_multinr(sys); */
 +
 +    if (EEL_PME(ir->coulombtype))
 +    {
 +        float ratio = pme_load_estimate(sys, ir, state.box);
 +        fprintf(stderr, "Estimate for the relative computational load of the PME mesh part: %.2f\n", ratio);
 +        /* With free energy we might need to do PME both for the A and B state
 +         * charges. This will double the cost, but the optimal performance will
 +         * then probably be at a slightly larger cut-off and grid spacing.
 +         */
 +        if ((ir->efep == efepNO && ratio > 1.0/2.0) ||
 +            (ir->efep != efepNO && ratio > 2.0/3.0))
 +        {
 +            warning_note(wi,
 +                         "The optimal PME mesh load for parallel simulations is below 0.5\n"
 +                         "and for highly parallel simulations between 0.25 and 0.33,\n"
 +                         "for higher performance, increase the cut-off and the PME grid spacing.\n");
 +            if (ir->efep != efepNO)
 +            {
 +                warning_note(wi,
 +                             "For free energy simulations, the optimal load limit increases from 0.5 to 0.667\n");
 +            }
 +        }
 +    }
 +
 +    {
 +        char   warn_buf[STRLEN];
 +        double cio = compute_io(ir, sys->natoms, &sys->groups, F_NRE, 1);
 +        sprintf(warn_buf, "This run will generate roughly %.0f Mb of data", cio);
 +        if (cio > 2000)
 +        {
 +            set_warning_line(wi, mdparin, -1);
 +            warning_note(wi, warn_buf);
 +        }
 +        else
 +        {
 +            printf("%s\n", warn_buf);
 +        }
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "writing run input file...\n");
 +    }
 +
 +    done_warning(wi, FARGS);
 +    write_tpx_state(ftp2fn(efTPX, NFILE, fnm), ir, &state, sys);
 +
 +    /* Output IMD group, if bIMD is TRUE */
 +    write_IMDgroup_to_file(ir->bIMD, ir, &state, sys, NFILE, fnm);
 +
 +    done_atomtype(atype);
 +    done_mtop(sys, TRUE);
 +    done_inputrec_strings();
 +
 +    return 0;
 +}
index 2d206d01dfa03044cfb03d33e9084c932f186427,0000000000000000000000000000000000000000..68a2a71d73129744caa8b6471698f4515461890a
mode 100644,000000..100644
--- /dev/null
@@@ -1,4390 -1,0 +1,4396 @@@
 +/*
 + * 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 "gromacs/utility/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 "gromacs/utility/cstringutil.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],
 +         imd_grp[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.
 + * NOTE: index groups are not set here yet, don't check things
 + * like temperature coupling group options here, but in triple_check
 + */
 +{
 +    /* 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)));
 +
 +        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));
 +    }
 +
 +    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 || ir->coulomb_modifier == eintmodPOTSWITCH)
 +    {
 +        if (ir->rcoulomb_switch/ir->rcoulomb < 0.9499)
 +        {
 +            real percentage  = 100*(ir->rcoulomb-ir->rcoulomb_switch)/ir->rcoulomb;
 +            sprintf(warn_buf, "The switching range for should be 5%% or less (currently %.2f%% using a switching range of %4f-%4f) for accurate electrostatic energies, energy conservation will be good regardless, since ewald_rtol = %g.",
 +                    percentage, ir->rcoulomb_switch, ir->rcoulomb, ir->ewald_rtol);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (ir->vdwtype == evdwSWITCH || ir->vdw_modifier == eintmodPOTSWITCH)
 +    {
 +        if (ir->rvdw_switch == 0)
 +        {
 +            sprintf(warn_buf, "rvdw-switch is equal 0 even though you are using a switched Lennard-Jones potential.  This suggests it was not set in the mdp, which can lead to large energy errors.  In GROMACS, 0.05 to 0.1 nm is often a reasonable vdw switching range.");
 +            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->vdwtype == evdwPME)
 +    {
 +        if (!(ir->vdw_modifier == eintmodNONE || ir->vdw_modifier == eintmodPOTSHIFT))
 +        {
 +            sprintf(err_buf, "With vdwtype = %s, the only supported modifiers are %s a\
 +nd %s",
 +                    evdw_names[ir->vdwtype],
 +                    eintmod_names[eintmodPOTSHIFT],
 +                    eintmod_names[eintmodNONE]);
 +        }
 +    }
 +
 +    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);
 +        }
 +    }
 +
++    if (EI_VV(ir->eI) && IR_TWINRANGE(*ir) && ir->nstlist > 1)
++    {
++        sprintf(warn_buf, "Twin-range multiple time stepping does not work with integrator %s.", ei_names[ir->eI]);
++        warning_error(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);
 +    }
 +
 +    /* Interactive MD */
 +    ir->bIMD = FALSE;
 +    CCTYPE("Group to display and/or manipulate in interactive MD session");
 +    STYPE ("IMD-group", is->imd_grp, NULL);
 +    if (is->imd_grp[0] != '\0')
 +    {
 +        snew(ir->imd, 1);
 +        ir->bIMD = TRUE;
 +    }
 +
 +    /* 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 make_IMD_group(t_IMD *IMDgroup, char *IMDgname, t_blocka *grps, char **gnames)
 +{
 +    int      ig = -1, i;
 +
 +
 +    ig            = search_string(IMDgname, grps->nr, gnames);
 +    IMDgroup->nat = grps->index[ig+1] - grps->index[ig];
 +
 +    if (IMDgroup->nat > 0)
 +    {
 +        fprintf(stderr, "Group '%s' with %d atoms can be activated for interactive molecular dynamics (IMD).\n",
 +                IMDgname, IMDgroup->nat);
 +        snew(IMDgroup->ind, IMDgroup->nat);
 +        for (i = 0; i < IMDgroup->nat; i++)
 +        {
 +            IMDgroup->ind[i] = grps->a[grps->index[ig]+i];
 +        }
 +    }
 +}
 +
 +
 +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);
 +    }
 +
 +    /* Make indices for IMD session */
 +    if (ir->bIMD)
 +    {
 +        make_IMD_group(ir->imd, is->imd_grp, 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[STRLEN];
 +    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 (ir->cutoff_scheme == ecutsVERLET &&
 +        ir->verletbuf_tol > 0 &&
 +        ir->nstlist > 1 &&
 +        ((EI_MD(ir->eI) || EI_SD(ir->eI)) &&
 +         (ir->etc == etcVRESCALE || ir->etc == etcBERENDSEN)))
 +    {
 +        /* Check if a too small Verlet buffer might potentially
 +         * cause more drift than the thermostat can couple off.
 +         */
 +        /* Temperature error fraction for warning and suggestion */
 +        const real T_error_warn    = 0.002;
 +        const real T_error_suggest = 0.001;
 +        /* For safety: 2 DOF per atom (typical with constraints) */
 +        const real nrdf_at         = 2;
 +        real       T, tau, max_T_error;
 +        int        i;
 +
 +        T   = 0;
 +        tau = 0;
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            T   = max(T, ir->opts.ref_t[i]);
 +            tau = max(tau, ir->opts.tau_t[i]);
 +        }
 +        if (T > 0)
 +        {
 +            /* This is a worst case estimate of the temperature error,
 +             * assuming perfect buffer estimation and no cancelation
 +             * of errors. The factor 0.5 is because energy distributes
 +             * equally over Ekin and Epot.
 +             */
 +            max_T_error = 0.5*tau*ir->verletbuf_tol/(nrdf_at*BOLTZ*T);
 +            if (max_T_error > T_error_warn)
 +            {
 +                sprintf(warn_buf, "With a verlet-buffer-tolerance of %g kJ/mol/ps, a reference temperature of %g and a tau_t of %g, your temperature might be off by up to %.1f%%. To ensure the error is below %.1f%%, decrease verlet-buffer-tolerance to %.0e or decrease tau_t.",
 +                        ir->verletbuf_tol, T, tau,
 +                        100*max_T_error,
 +                        100*T_error_suggest,
 +                        ir->verletbuf_tol*T_error_suggest/max_T_error);
 +                warning(wi, warn_buf);
 +            }
 +        }
 +    }
 +
 +    if (ETC_ANDERSEN(ir->etc))
 +    {
 +        int i;
 +
 +        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);
 +        }
 +
 +        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 (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 67cd36f34184ba820b4ab4a6196dd56fc135078a,0000000000000000000000000000000000000000..8db62ab92d8cc649abe64b7c8ae31f4cefd44611
mode 100644,000000..100644
--- /dev/null
@@@ -1,3283 -1,0 +1,3288 @@@
 +/*
 + * 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 <math.h>
 +#include <string.h>
 +#include <assert.h>
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "vec.h"
 +#include "gromacs/math/utilities.h"
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "physics.h"
 +#include "force.h"
 +#include "tables.h"
 +#include "nonbonded.h"
 +#include "invblock.h"
 +#include "names.h"
 +#include "network.h"
 +#include "pbc.h"
 +#include "ns.h"
 +#include "mshift.h"
 +#include "txtdump.h"
 +#include "coulomb.h"
 +#include "md_support.h"
 +#include "md_logging.h"
 +#include "domdec.h"
 +#include "qmmm.h"
 +#include "copyrite.h"
 +#include "mtop_util.h"
 +#include "nbnxn_simd.h"
 +#include "nbnxn_search.h"
 +#include "nbnxn_atomdata.h"
 +#include "nbnxn_consts.h"
 +#include "gmx_omp_nthreads.h"
 +#include "gmx_detect_hardware.h"
 +#include "inputrec.h"
 +
 +#include "types/nbnxn_cuda_types_ext.h"
 +#include "gpu_utils.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +#include "pmalloc_cuda.h"
 +
 +t_forcerec *mk_forcerec(void)
 +{
 +    t_forcerec *fr;
 +
 +    snew(fr, 1);
 +
 +    return fr;
 +}
 +
 +#ifdef DEBUG
 +static void pr_nbfp(FILE *fp, real *nbfp, gmx_bool bBHAM, int atnr)
 +{
 +    int i, j;
 +
 +    for (i = 0; (i < atnr); i++)
 +    {
 +        for (j = 0; (j < atnr); j++)
 +        {
 +            fprintf(fp, "%2d - %2d", i, j);
 +            if (bBHAM)
 +            {
 +                fprintf(fp, "  a=%10g, b=%10g, c=%10g\n", BHAMA(nbfp, atnr, i, j),
 +                        BHAMB(nbfp, atnr, i, j), BHAMC(nbfp, atnr, i, j)/6.0);
 +            }
 +            else
 +            {
 +                fprintf(fp, "  c6=%10g, c12=%10g\n", C6(nbfp, atnr, i, j)/6.0,
 +                        C12(nbfp, atnr, i, j)/12.0);
 +            }
 +        }
 +    }
 +}
 +#endif
 +
 +static real *mk_nbfp(const gmx_ffparams_t *idef, gmx_bool bBHAM)
 +{
 +    real *nbfp;
 +    int   i, j, k, atnr;
 +
 +    atnr = idef->atnr;
 +    if (bBHAM)
 +    {
 +        snew(nbfp, 3*atnr*atnr);
 +        for (i = k = 0; (i < atnr); i++)
 +        {
 +            for (j = 0; (j < atnr); j++, k++)
 +            {
 +                BHAMA(nbfp, atnr, i, j) = idef->iparams[k].bham.a;
 +                BHAMB(nbfp, atnr, i, j) = idef->iparams[k].bham.b;
 +                /* nbfp now includes the 6.0 derivative prefactor */
 +                BHAMC(nbfp, atnr, i, j) = idef->iparams[k].bham.c*6.0;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        snew(nbfp, 2*atnr*atnr);
 +        for (i = k = 0; (i < atnr); i++)
 +        {
 +            for (j = 0; (j < atnr); j++, k++)
 +            {
 +                /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                C6(nbfp, atnr, i, j)   = idef->iparams[k].lj.c6*6.0;
 +                C12(nbfp, atnr, i, j)  = idef->iparams[k].lj.c12*12.0;
 +            }
 +        }
 +    }
 +
 +    return nbfp;
 +}
 +
 +static real *make_ljpme_c6grid(const gmx_ffparams_t *idef, t_forcerec *fr)
 +{
 +    int   i, j, k, atnr;
 +    real  c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 +    real *grid;
 +
 +    /* For LJ-PME simulations, we correct the energies with the reciprocal space
 +     * inside of the cut-off. To do this the non-bonded kernels needs to have
 +     * access to the C6-values used on the reciprocal grid in pme.c
 +     */
 +
 +    atnr = idef->atnr;
 +    snew(grid, 2*atnr*atnr);
 +    for (i = k = 0; (i < atnr); i++)
 +    {
 +        for (j = 0; (j < atnr); j++, k++)
 +        {
 +            c6i  = idef->iparams[i*(atnr+1)].lj.c6;
 +            c12i = idef->iparams[i*(atnr+1)].lj.c12;
 +            c6j  = idef->iparams[j*(atnr+1)].lj.c6;
 +            c12j = idef->iparams[j*(atnr+1)].lj.c12;
 +            c6   = sqrt(c6i * c6j);
 +            if (fr->ljpme_combination_rule == eljpmeLB
 +                && !gmx_numzero(c6) && !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 / c12i;
 +                epsj   = c6j * c6j / c12j;
 +                c6     = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
 +            }
 +            /* Store the elements at the same relative positions as C6 in nbfp in order
 +             * to simplify access in the kernels
 +             */
 +            grid[2*(atnr*i+j)] = c6*6.0;
 +        }
 +    }
 +    return grid;
 +}
 +
 +static real *mk_nbfp_combination_rule(const gmx_ffparams_t *idef, int comb_rule)
 +{
 +    real *nbfp;
 +    int   i, j, k, atnr;
 +    real  c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 +    real  c6, c12;
 +
 +    atnr = idef->atnr;
 +    snew(nbfp, 2*atnr*atnr);
 +    for (i = 0; i < atnr; ++i)
 +    {
 +        for (j = 0; j < atnr; ++j)
 +        {
 +            c6i  = idef->iparams[i*(atnr+1)].lj.c6;
 +            c12i = idef->iparams[i*(atnr+1)].lj.c12;
 +            c6j  = idef->iparams[j*(atnr+1)].lj.c6;
 +            c12j = idef->iparams[j*(atnr+1)].lj.c12;
 +            c6   = sqrt(c6i  * c6j);
 +            c12  = sqrt(c12i * c12j);
 +            if (comb_rule == eCOMB_ARITHMETIC
 +                && !gmx_numzero(c6) && !gmx_numzero(c12))
 +            {
 +                sigmai = pow(c12i / c6i, 1.0/6.0);
 +                sigmaj = pow(c12j / c6j, 1.0/6.0);
 +                epsi   = c6i * c6i / c12i;
 +                epsj   = c6j * c6j / c12j;
 +                c6     = epsi * epsj * pow(0.5*(sigmai+sigmaj), 6);
 +                c12    = epsi * epsj * pow(0.5*(sigmai+sigmaj), 12);
 +            }
 +            C6(nbfp, atnr, i, j)   = c6*6.0;
 +            C12(nbfp, atnr, i, j)  = c12*12.0;
 +        }
 +    }
 +    return nbfp;
 +}
 +
 +/* This routine sets fr->solvent_opt to the most common solvent in the
 + * system, e.g. esolSPC or esolTIP4P. It will also mark each charge group in
 + * the fr->solvent_type array with the correct type (or esolNO).
 + *
 + * Charge groups that fulfill the conditions but are not identical to the
 + * most common one will be marked as esolNO in the solvent_type array.
 + *
 + * TIP3p is identical to SPC for these purposes, so we call it
 + * SPC in the arrays (Apologies to Bill Jorgensen ;-)
 + *
 + * NOTE: QM particle should not
 + * become an optimized solvent. Not even if there is only one charge
 + * group in the Qm
 + */
 +
 +typedef struct
 +{
 +    int    model;
 +    int    count;
 +    int    vdwtype[4];
 +    real   charge[4];
 +} solvent_parameters_t;
 +
 +static void
 +check_solvent_cg(const gmx_moltype_t    *molt,
 +                 int                     cg0,
 +                 int                     nmol,
 +                 const unsigned char    *qm_grpnr,
 +                 const t_grps           *qm_grps,
 +                 t_forcerec   *          fr,
 +                 int                    *n_solvent_parameters,
 +                 solvent_parameters_t  **solvent_parameters_p,
 +                 int                     cginfo,
 +                 int                    *cg_sp)
 +{
 +    const t_blocka       *excl;
 +    t_atom               *atom;
 +    int                   j, k;
 +    int                   j0, j1, nj;
 +    gmx_bool              perturbed;
 +    gmx_bool              has_vdw[4];
 +    gmx_bool              match;
 +    real                  tmp_charge[4]  = { 0.0 }; /* init to zero to make gcc4.8 happy */
 +    int                   tmp_vdwtype[4] = { 0 };   /* init to zero to make gcc4.8 happy */
 +    int                   tjA;
 +    gmx_bool              qm;
 +    solvent_parameters_t *solvent_parameters;
 +
 +    /* We use a list with parameters for each solvent type.
 +     * Every time we discover a new molecule that fulfills the basic
 +     * conditions for a solvent we compare with the previous entries
 +     * in these lists. If the parameters are the same we just increment
 +     * the counter for that type, and otherwise we create a new type
 +     * based on the current molecule.
 +     *
 +     * Once we've finished going through all molecules we check which
 +     * solvent is most common, and mark all those molecules while we
 +     * clear the flag on all others.
 +     */
 +
 +    solvent_parameters = *solvent_parameters_p;
 +
 +    /* Mark the cg first as non optimized */
 +    *cg_sp = -1;
 +
 +    /* Check if this cg has no exclusions with atoms in other charge groups
 +     * and all atoms inside the charge group excluded.
 +     * We only have 3 or 4 atom solvent loops.
 +     */
 +    if (GET_CGINFO_EXCL_INTER(cginfo) ||
 +        !GET_CGINFO_EXCL_INTRA(cginfo))
 +    {
 +        return;
 +    }
 +
 +    /* Get the indices of the first atom in this charge group */
 +    j0     = molt->cgs.index[cg0];
 +    j1     = molt->cgs.index[cg0+1];
 +
 +    /* Number of atoms in our molecule */
 +    nj     = j1 - j0;
 +
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Moltype '%s': there are %d atoms in this charge group\n",
 +                *molt->name, nj);
 +    }
 +
 +    /* Check if it could be an SPC (3 atoms) or TIP4p (4) water,
 +     * otherwise skip it.
 +     */
 +    if (nj < 3 || nj > 4)
 +    {
 +        return;
 +    }
 +
 +    /* Check if we are doing QM on this group */
 +    qm = FALSE;
 +    if (qm_grpnr != NULL)
 +    {
 +        for (j = j0; j < j1 && !qm; j++)
 +        {
 +            qm = (qm_grpnr[j] < qm_grps->nr - 1);
 +        }
 +    }
 +    /* Cannot use solvent optimization with QM */
 +    if (qm)
 +    {
 +        return;
 +    }
 +
 +    atom = molt->atoms.atom;
 +
 +    /* Still looks like a solvent, time to check parameters */
 +
 +    /* If it is perturbed (free energy) we can't use the solvent loops,
 +     * so then we just skip to the next molecule.
 +     */
 +    perturbed = FALSE;
 +
 +    for (j = j0; j < j1 && !perturbed; j++)
 +    {
 +        perturbed = PERTURBED(atom[j]);
 +    }
 +
 +    if (perturbed)
 +    {
 +        return;
 +    }
 +
 +    /* Now it's only a question if the VdW and charge parameters
 +     * are OK. Before doing the check we compare and see if they are
 +     * identical to a possible previous solvent type.
 +     * First we assign the current types and charges.
 +     */
 +    for (j = 0; j < nj; j++)
 +    {
 +        tmp_vdwtype[j] = atom[j0+j].type;
 +        tmp_charge[j]  = atom[j0+j].q;
 +    }
 +
 +    /* Does it match any previous solvent type? */
 +    for (k = 0; k < *n_solvent_parameters; k++)
 +    {
 +        match = TRUE;
 +
 +
 +        /* We can only match SPC with 3 atoms and TIP4p with 4 atoms */
 +        if ( (solvent_parameters[k].model == esolSPC   && nj != 3)  ||
 +             (solvent_parameters[k].model == esolTIP4P && nj != 4) )
 +        {
 +            match = FALSE;
 +        }
 +
 +        /* Check that types & charges match for all atoms in molecule */
 +        for (j = 0; j < nj && match == TRUE; j++)
 +        {
 +            if (tmp_vdwtype[j] != solvent_parameters[k].vdwtype[j])
 +            {
 +                match = FALSE;
 +            }
 +            if (tmp_charge[j] != solvent_parameters[k].charge[j])
 +            {
 +                match = FALSE;
 +            }
 +        }
 +        if (match == TRUE)
 +        {
 +            /* Congratulations! We have a matched solvent.
 +             * Flag it with this type for later processing.
 +             */
 +            *cg_sp = k;
 +            solvent_parameters[k].count += nmol;
 +
 +            /* We are done with this charge group */
 +            return;
 +        }
 +    }
 +
 +    /* If we get here, we have a tentative new solvent type.
 +     * Before we add it we must check that it fulfills the requirements
 +     * of the solvent optimized loops. First determine which atoms have
 +     * VdW interactions.
 +     */
 +    for (j = 0; j < nj; j++)
 +    {
 +        has_vdw[j] = FALSE;
 +        tjA        = tmp_vdwtype[j];
 +
 +        /* Go through all other tpes and see if any have non-zero
 +         * VdW parameters when combined with this one.
 +         */
 +        for (k = 0; k < fr->ntype && (has_vdw[j] == FALSE); k++)
 +        {
 +            /* We already checked that the atoms weren't perturbed,
 +             * so we only need to check state A now.
 +             */
 +            if (fr->bBHAM)
 +            {
 +                has_vdw[j] = (has_vdw[j] ||
 +                              (BHAMA(fr->nbfp, fr->ntype, tjA, k) != 0.0) ||
 +                              (BHAMB(fr->nbfp, fr->ntype, tjA, k) != 0.0) ||
 +                              (BHAMC(fr->nbfp, fr->ntype, tjA, k) != 0.0));
 +            }
 +            else
 +            {
 +                /* Standard LJ */
 +                has_vdw[j] = (has_vdw[j] ||
 +                              (C6(fr->nbfp, fr->ntype, tjA, k)  != 0.0) ||
 +                              (C12(fr->nbfp, fr->ntype, tjA, k) != 0.0));
 +            }
 +        }
 +    }
 +
 +    /* Now we know all we need to make the final check and assignment. */
 +    if (nj == 3)
 +    {
 +        /* So, is it an SPC?
 +         * For this we require thatn all atoms have charge,
 +         * the charges on atom 2 & 3 should be the same, and only
 +         * atom 1 might have VdW.
 +         */
 +        if (has_vdw[1] == FALSE &&
 +            has_vdw[2] == FALSE &&
 +            tmp_charge[0]  != 0 &&
 +            tmp_charge[1]  != 0 &&
 +            tmp_charge[2]  == tmp_charge[1])
 +        {
 +            srenew(solvent_parameters, *n_solvent_parameters+1);
 +            solvent_parameters[*n_solvent_parameters].model = esolSPC;
 +            solvent_parameters[*n_solvent_parameters].count = nmol;
 +            for (k = 0; k < 3; k++)
 +            {
 +                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 +                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 +            }
 +
 +            *cg_sp = *n_solvent_parameters;
 +            (*n_solvent_parameters)++;
 +        }
 +    }
 +    else if (nj == 4)
 +    {
 +        /* Or could it be a TIP4P?
 +         * For this we require thatn atoms 2,3,4 have charge, but not atom 1.
 +         * Only atom 1 mght have VdW.
 +         */
 +        if (has_vdw[1] == FALSE &&
 +            has_vdw[2] == FALSE &&
 +            has_vdw[3] == FALSE &&
 +            tmp_charge[0]  == 0 &&
 +            tmp_charge[1]  != 0 &&
 +            tmp_charge[2]  == tmp_charge[1] &&
 +            tmp_charge[3]  != 0)
 +        {
 +            srenew(solvent_parameters, *n_solvent_parameters+1);
 +            solvent_parameters[*n_solvent_parameters].model = esolTIP4P;
 +            solvent_parameters[*n_solvent_parameters].count = nmol;
 +            for (k = 0; k < 4; k++)
 +            {
 +                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 +                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 +            }
 +
 +            *cg_sp = *n_solvent_parameters;
 +            (*n_solvent_parameters)++;
 +        }
 +    }
 +
 +    *solvent_parameters_p = solvent_parameters;
 +}
 +
 +static void
 +check_solvent(FILE  *                fp,
 +              const gmx_mtop_t  *    mtop,
 +              t_forcerec  *          fr,
 +              cginfo_mb_t           *cginfo_mb)
 +{
 +    const t_block     *   cgs;
 +    const t_block     *   mols;
 +    const gmx_moltype_t  *molt;
 +    int                   mb, mol, cg_mol, at_offset, cg_offset, am, cgm, i, nmol_ch, nmol;
 +    int                   n_solvent_parameters;
 +    solvent_parameters_t *solvent_parameters;
 +    int                 **cg_sp;
 +    int                   bestsp, bestsol;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Going to determine what solvent types we have.\n");
 +    }
 +
 +    mols = &mtop->mols;
 +
 +    n_solvent_parameters = 0;
 +    solvent_parameters   = NULL;
 +    /* Allocate temporary array for solvent type */
 +    snew(cg_sp, mtop->nmolblock);
 +
 +    cg_offset = 0;
 +    at_offset = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +        cgs  = &molt->cgs;
 +        /* Here we have to loop over all individual molecules
 +         * because we need to check for QMMM particles.
 +         */
 +        snew(cg_sp[mb], cginfo_mb[mb].cg_mod);
 +        nmol_ch = cginfo_mb[mb].cg_mod/cgs->nr;
 +        nmol    = mtop->molblock[mb].nmol/nmol_ch;
 +        for (mol = 0; mol < nmol_ch; mol++)
 +        {
 +            cgm = mol*cgs->nr;
 +            am  = mol*cgs->index[cgs->nr];
 +            for (cg_mol = 0; cg_mol < cgs->nr; cg_mol++)
 +            {
 +                check_solvent_cg(molt, cg_mol, nmol,
 +                                 mtop->groups.grpnr[egcQMMM] ?
 +                                 mtop->groups.grpnr[egcQMMM]+at_offset+am : 0,
 +                                 &mtop->groups.grps[egcQMMM],
 +                                 fr,
 +                                 &n_solvent_parameters, &solvent_parameters,
 +                                 cginfo_mb[mb].cginfo[cgm+cg_mol],
 +                                 &cg_sp[mb][cgm+cg_mol]);
 +            }
 +        }
 +        cg_offset += cgs->nr;
 +        at_offset += cgs->index[cgs->nr];
 +    }
 +
 +    /* Puh! We finished going through all charge groups.
 +     * Now find the most common solvent model.
 +     */
 +
 +    /* Most common solvent this far */
 +    bestsp = -2;
 +    for (i = 0; i < n_solvent_parameters; i++)
 +    {
 +        if (bestsp == -2 ||
 +            solvent_parameters[i].count > solvent_parameters[bestsp].count)
 +        {
 +            bestsp = i;
 +        }
 +    }
 +
 +    if (bestsp >= 0)
 +    {
 +        bestsol = solvent_parameters[bestsp].model;
 +    }
 +    else
 +    {
 +        bestsol = esolNO;
 +    }
 +
 +#ifdef DISABLE_WATER_NLIST
 +    bestsol = esolNO;
 +#endif
 +
 +    fr->nWatMol = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        cgs  = &mtop->moltype[mtop->molblock[mb].type].cgs;
 +        nmol = (mtop->molblock[mb].nmol*cgs->nr)/cginfo_mb[mb].cg_mod;
 +        for (i = 0; i < cginfo_mb[mb].cg_mod; i++)
 +        {
 +            if (cg_sp[mb][i] == bestsp)
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], bestsol);
 +                fr->nWatMol += nmol;
 +            }
 +            else
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], esolNO);
 +            }
 +        }
 +        sfree(cg_sp[mb]);
 +    }
 +    sfree(cg_sp);
 +
 +    if (bestsol != esolNO && fp != NULL)
 +    {
 +        fprintf(fp, "\nEnabling %s-like water optimization for %d molecules.\n\n",
 +                esol_names[bestsol],
 +                solvent_parameters[bestsp].count);
 +    }
 +
 +    sfree(solvent_parameters);
 +    fr->solvent_opt = bestsol;
 +}
 +
 +enum {
 +    acNONE = 0, acCONSTRAINT, acSETTLE
 +};
 +
 +static cginfo_mb_t *init_cginfo_mb(FILE *fplog, const gmx_mtop_t *mtop,
 +                                   t_forcerec *fr, gmx_bool bNoSolvOpt,
 +                                   gmx_bool *bFEP_NonBonded,
 +                                   gmx_bool *bExcl_IntraCGAll_InterCGNone)
 +{
 +    const t_block        *cgs;
 +    const t_blocka       *excl;
 +    const gmx_moltype_t  *molt;
 +    const gmx_molblock_t *molb;
 +    cginfo_mb_t          *cginfo_mb;
 +    gmx_bool             *type_VDW;
 +    int                  *cginfo;
 +    int                   cg_offset, a_offset, cgm, am;
 +    int                   mb, m, ncg_tot, cg, a0, a1, gid, ai, j, aj, excl_nalloc;
 +    int                  *a_con;
 +    int                   ftype;
 +    int                   ia;
 +    gmx_bool              bId, *bExcl, bExclIntraAll, bExclInter, bHaveVDW, bHaveQ, bHavePerturbedAtoms;
 +
 +    ncg_tot = ncg_mtop(mtop);
 +    snew(cginfo_mb, mtop->nmolblock);
 +
 +    snew(type_VDW, fr->ntype);
 +    for (ai = 0; ai < fr->ntype; ai++)
 +    {
 +        type_VDW[ai] = FALSE;
 +        for (j = 0; j < fr->ntype; j++)
 +        {
 +            type_VDW[ai] = type_VDW[ai] ||
 +                fr->bBHAM ||
 +                C6(fr->nbfp, fr->ntype, ai, j) != 0 ||
 +                C12(fr->nbfp, fr->ntype, ai, j) != 0;
 +        }
 +    }
 +
 +    *bFEP_NonBonded               = FALSE;
 +    *bExcl_IntraCGAll_InterCGNone = TRUE;
 +
 +    excl_nalloc = 10;
 +    snew(bExcl, excl_nalloc);
 +    cg_offset = 0;
 +    a_offset  = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        molt = &mtop->moltype[molb->type];
 +        cgs  = &molt->cgs;
 +        excl = &molt->excls;
 +
 +        /* Check if the cginfo is identical for all molecules in this block.
 +         * If so, we only need an array of the size of one molecule.
 +         * Otherwise we make an array of #mol times #cgs per molecule.
 +         */
 +        bId = TRUE;
 +        am  = 0;
 +        for (m = 0; m < molb->nmol; m++)
 +        {
 +            am = m*cgs->index[cgs->nr];
 +            for (cg = 0; cg < cgs->nr; cg++)
 +            {
 +                a0 = cgs->index[cg];
 +                a1 = cgs->index[cg+1];
 +                if (ggrpnr(&mtop->groups, egcENER, a_offset+am+a0) !=
 +                    ggrpnr(&mtop->groups, egcENER, a_offset   +a0))
 +                {
 +                    bId = FALSE;
 +                }
 +                if (mtop->groups.grpnr[egcQMMM] != NULL)
 +                {
 +                    for (ai = a0; ai < a1; ai++)
 +                    {
 +                        if (mtop->groups.grpnr[egcQMMM][a_offset+am+ai] !=
 +                            mtop->groups.grpnr[egcQMMM][a_offset   +ai])
 +                        {
 +                            bId = FALSE;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        cginfo_mb[mb].cg_start = cg_offset;
 +        cginfo_mb[mb].cg_end   = cg_offset + molb->nmol*cgs->nr;
 +        cginfo_mb[mb].cg_mod   = (bId ? 1 : molb->nmol)*cgs->nr;
 +        snew(cginfo_mb[mb].cginfo, cginfo_mb[mb].cg_mod);
 +        cginfo = cginfo_mb[mb].cginfo;
 +
 +        /* Set constraints flags for constrained atoms */
 +        snew(a_con, molt->atoms.nr);
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (interaction_function[ftype].flags & IF_CONSTRAINT)
 +            {
 +                int nral;
 +
 +                nral = NRAL(ftype);
 +                for (ia = 0; ia < molt->ilist[ftype].nr; ia += 1+nral)
 +                {
 +                    int a;
 +
 +                    for (a = 0; a < nral; a++)
 +                    {
 +                        a_con[molt->ilist[ftype].iatoms[ia+1+a]] =
 +                            (ftype == F_SETTLE ? acSETTLE : acCONSTRAINT);
 +                    }
 +                }
 +            }
 +        }
 +
 +        for (m = 0; m < (bId ? 1 : molb->nmol); m++)
 +        {
 +            cgm = m*cgs->nr;
 +            am  = m*cgs->index[cgs->nr];
 +            for (cg = 0; cg < cgs->nr; cg++)
 +            {
 +                a0 = cgs->index[cg];
 +                a1 = cgs->index[cg+1];
 +
 +                /* Store the energy group in cginfo */
 +                gid = ggrpnr(&mtop->groups, egcENER, a_offset+am+a0);
 +                SET_CGINFO_GID(cginfo[cgm+cg], gid);
 +
 +                /* Check the intra/inter charge group exclusions */
 +                if (a1-a0 > excl_nalloc)
 +                {
 +                    excl_nalloc = a1 - a0;
 +                    srenew(bExcl, excl_nalloc);
 +                }
 +                /* bExclIntraAll: all intra cg interactions excluded
 +                 * bExclInter:    any inter cg interactions excluded
 +                 */
 +                bExclIntraAll       = TRUE;
 +                bExclInter          = FALSE;
 +                bHaveVDW            = FALSE;
 +                bHaveQ              = FALSE;
 +                bHavePerturbedAtoms = FALSE;
 +                for (ai = a0; ai < a1; ai++)
 +                {
 +                    /* Check VDW and electrostatic interactions */
 +                    bHaveVDW = bHaveVDW || (type_VDW[molt->atoms.atom[ai].type] ||
 +                                            type_VDW[molt->atoms.atom[ai].typeB]);
 +                    bHaveQ  = bHaveQ    || (molt->atoms.atom[ai].q != 0 ||
 +                                            molt->atoms.atom[ai].qB != 0);
 +
 +                    bHavePerturbedAtoms = bHavePerturbedAtoms || (PERTURBED(molt->atoms.atom[ai]) != 0);
 +
 +                    /* Clear the exclusion list for atom ai */
 +                    for (aj = a0; aj < a1; aj++)
 +                    {
 +                        bExcl[aj-a0] = FALSE;
 +                    }
 +                    /* Loop over all the exclusions of atom ai */
 +                    for (j = excl->index[ai]; j < excl->index[ai+1]; j++)
 +                    {
 +                        aj = excl->a[j];
 +                        if (aj < a0 || aj >= a1)
 +                        {
 +                            bExclInter = TRUE;
 +                        }
 +                        else
 +                        {
 +                            bExcl[aj-a0] = TRUE;
 +                        }
 +                    }
 +                    /* Check if ai excludes a0 to a1 */
 +                    for (aj = a0; aj < a1; aj++)
 +                    {
 +                        if (!bExcl[aj-a0])
 +                        {
 +                            bExclIntraAll = FALSE;
 +                        }
 +                    }
 +
 +                    switch (a_con[ai])
 +                    {
 +                        case acCONSTRAINT:
 +                            SET_CGINFO_CONSTR(cginfo[cgm+cg]);
 +                            break;
 +                        case acSETTLE:
 +                            SET_CGINFO_SETTLE(cginfo[cgm+cg]);
 +                            break;
 +                        default:
 +                            break;
 +                    }
 +                }
 +                if (bExclIntraAll)
 +                {
 +                    SET_CGINFO_EXCL_INTRA(cginfo[cgm+cg]);
 +                }
 +                if (bExclInter)
 +                {
 +                    SET_CGINFO_EXCL_INTER(cginfo[cgm+cg]);
 +                }
 +                if (a1 - a0 > MAX_CHARGEGROUP_SIZE)
 +                {
 +                    /* The size in cginfo is currently only read with DD */
 +                    gmx_fatal(FARGS, "A charge group has size %d which is larger than the limit of %d atoms", a1-a0, MAX_CHARGEGROUP_SIZE);
 +                }
 +                if (bHaveVDW)
 +                {
 +                    SET_CGINFO_HAS_VDW(cginfo[cgm+cg]);
 +                }
 +                if (bHaveQ)
 +                {
 +                    SET_CGINFO_HAS_Q(cginfo[cgm+cg]);
 +                }
 +                if (bHavePerturbedAtoms && fr->efep != efepNO)
 +                {
 +                    SET_CGINFO_FEP(cginfo[cgm+cg]);
 +                    *bFEP_NonBonded = TRUE;
 +                }
 +                /* Store the charge group size */
 +                SET_CGINFO_NATOMS(cginfo[cgm+cg], a1-a0);
 +
 +                if (!bExclIntraAll || bExclInter)
 +                {
 +                    *bExcl_IntraCGAll_InterCGNone = FALSE;
 +                }
 +            }
 +        }
 +
 +        sfree(a_con);
 +
 +        cg_offset += molb->nmol*cgs->nr;
 +        a_offset  += molb->nmol*cgs->index[cgs->nr];
 +    }
 +    sfree(bExcl);
 +
 +    /* the solvent optimizer is called after the QM is initialized,
 +     * because we don't want to have the QM subsystemto become an
 +     * optimized solvent
 +     */
 +
 +    check_solvent(fplog, mtop, fr, cginfo_mb);
 +
 +    if (getenv("GMX_NO_SOLV_OPT"))
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Found environment variable GMX_NO_SOLV_OPT.\n"
 +                    "Disabling all solvent optimization\n");
 +        }
 +        fr->solvent_opt = esolNO;
 +    }
 +    if (bNoSolvOpt)
 +    {
 +        fr->solvent_opt = esolNO;
 +    }
 +    if (!fr->solvent_opt)
 +    {
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            for (cg = 0; cg < cginfo_mb[mb].cg_mod; cg++)
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[cg], esolNO);
 +            }
 +        }
 +    }
 +
 +    return cginfo_mb;
 +}
 +
 +static int *cginfo_expand(int nmb, cginfo_mb_t *cgi_mb)
 +{
 +    int  ncg, mb, cg;
 +    int *cginfo;
 +
 +    ncg = cgi_mb[nmb-1].cg_end;
 +    snew(cginfo, ncg);
 +    mb = 0;
 +    for (cg = 0; cg < ncg; cg++)
 +    {
 +        while (cg >= cgi_mb[mb].cg_end)
 +        {
 +            mb++;
 +        }
 +        cginfo[cg] =
 +            cgi_mb[mb].cginfo[(cg - cgi_mb[mb].cg_start) % cgi_mb[mb].cg_mod];
 +    }
 +
 +    return cginfo;
 +}
 +
 +static void set_chargesum(FILE *log, t_forcerec *fr, const gmx_mtop_t *mtop)
 +{
 +    /*This now calculates sum for q and c6*/
 +    double         qsum, q2sum, q, c6sum, c6;
 +    int            mb, nmol, i;
 +    const t_atoms *atoms;
 +
 +    qsum   = 0;
 +    q2sum  = 0;
 +    c6sum  = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        nmol  = mtop->molblock[mb].nmol;
 +        atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +        for (i = 0; i < atoms->nr; i++)
 +        {
 +            q       = atoms->atom[i].q;
 +            qsum   += nmol*q;
 +            q2sum  += nmol*q*q;
 +            c6      = mtop->ffparams.iparams[atoms->atom[i].type*(mtop->ffparams.atnr+1)].lj.c6;
 +            c6sum  += nmol*c6;
 +        }
 +    }
 +    fr->qsum[0]   = qsum;
 +    fr->q2sum[0]  = q2sum;
 +    fr->c6sum[0]  = c6sum;
 +
 +    if (fr->efep != efepNO)
 +    {
 +        qsum   = 0;
 +        q2sum  = 0;
 +        c6sum  = 0;
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            nmol  = mtop->molblock[mb].nmol;
 +            atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +            for (i = 0; i < atoms->nr; i++)
 +            {
 +                q       = atoms->atom[i].qB;
 +                qsum   += nmol*q;
 +                q2sum  += nmol*q*q;
 +                c6      = mtop->ffparams.iparams[atoms->atom[i].typeB*(mtop->ffparams.atnr+1)].lj.c6;
 +                c6sum  += nmol*c6;
 +            }
 +            fr->qsum[1]   = qsum;
 +            fr->q2sum[1]  = q2sum;
 +            fr->c6sum[1]  = c6sum;
 +        }
 +    }
 +    else
 +    {
 +        fr->qsum[1]   = fr->qsum[0];
 +        fr->q2sum[1]  = fr->q2sum[0];
 +        fr->c6sum[1]  = fr->c6sum[0];
 +    }
 +    if (log)
 +    {
 +        if (fr->efep == efepNO)
 +        {
 +            fprintf(log, "System total charge: %.3f\n", fr->qsum[0]);
 +        }
 +        else
 +        {
 +            fprintf(log, "System total charge, top. A: %.3f top. B: %.3f\n",
 +                    fr->qsum[0], fr->qsum[1]);
 +        }
 +    }
 +}
 +
 +void update_forcerec(t_forcerec *fr, matrix box)
 +{
 +    if (fr->eeltype == eelGRF)
 +    {
 +        calc_rffac(NULL, fr->eeltype, fr->epsilon_r, fr->epsilon_rf,
 +                   fr->rcoulomb, fr->temp, fr->zsquare, box,
 +                   &fr->kappa, &fr->k_rf, &fr->c_rf);
 +    }
 +}
 +
 +void set_avcsixtwelve(FILE *fplog, t_forcerec *fr, const gmx_mtop_t *mtop)
 +{
 +    const t_atoms  *atoms, *atoms_tpi;
 +    const t_blocka *excl;
 +    int             mb, nmol, nmolc, i, j, tpi, tpj, j1, j2, k, n, nexcl, q;
 +    gmx_int64_t     npair, npair_ij, tmpi, tmpj;
 +    double          csix, ctwelve;
 +    int             ntp, *typecount;
 +    gmx_bool        bBHAM;
 +    real           *nbfp;
 +    real           *nbfp_comb = NULL;
 +
 +    ntp   = fr->ntype;
 +    bBHAM = fr->bBHAM;
 +    nbfp  = fr->nbfp;
 +
 +    /* For LJ-PME, we want to correct for the difference between the
 +     * actual C6 values and the C6 values used by the LJ-PME based on
 +     * combination rules. */
 +
 +    if (EVDW_PME(fr->vdwtype))
 +    {
 +        nbfp_comb = mk_nbfp_combination_rule(&mtop->ffparams,
 +                                             (fr->ljpme_combination_rule == eljpmeLB) ? eCOMB_ARITHMETIC : eCOMB_GEOMETRIC);
 +        for (tpi = 0; tpi < ntp; ++tpi)
 +        {
 +            for (tpj = 0; tpj < ntp; ++tpj)
 +            {
 +                C6(nbfp_comb, ntp, tpi, tpj) =
 +                    C6(nbfp, ntp, tpi, tpj) - C6(nbfp_comb, ntp, tpi, tpj);
 +                C12(nbfp_comb, ntp, tpi, tpj) = C12(nbfp, ntp, tpi, tpj);
 +            }
 +        }
 +        nbfp = nbfp_comb;
 +    }
 +    for (q = 0; q < (fr->efep == efepNO ? 1 : 2); q++)
 +    {
 +        csix    = 0;
 +        ctwelve = 0;
 +        npair   = 0;
 +        nexcl   = 0;
 +        if (!fr->n_tpi)
 +        {
 +            /* Count the types so we avoid natoms^2 operations */
 +            snew(typecount, ntp);
 +            gmx_mtop_count_atomtypes(mtop, q, typecount);
 +
 +            for (tpi = 0; tpi < ntp; tpi++)
 +            {
 +                for (tpj = tpi; tpj < ntp; tpj++)
 +                {
 +                    tmpi = typecount[tpi];
 +                    tmpj = typecount[tpj];
 +                    if (tpi != tpj)
 +                    {
 +                        npair_ij = tmpi*tmpj;
 +                    }
 +                    else
 +                    {
 +                        npair_ij = tmpi*(tmpi - 1)/2;
 +                    }
 +                    if (bBHAM)
 +                    {
 +                        /* nbfp now includes the 6.0 derivative prefactor */
 +                        csix    += npair_ij*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 +                    }
 +                    else
 +                    {
 +                        /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                        csix    += npair_ij*   C6(nbfp, ntp, tpi, tpj)/6.0;
 +                        ctwelve += npair_ij*  C12(nbfp, ntp, tpi, tpj)/12.0;
 +                    }
 +                    npair += npair_ij;
 +                }
 +            }
 +            sfree(typecount);
 +            /* Subtract the excluded pairs.
 +             * The main reason for substracting exclusions is that in some cases
 +             * some combinations might never occur and the parameters could have
 +             * any value. These unused values should not influence the dispersion
 +             * correction.
 +             */
 +            for (mb = 0; mb < mtop->nmolblock; mb++)
 +            {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                excl  = &mtop->moltype[mtop->molblock[mb].type].excls;
 +                for (i = 0; (i < atoms->nr); i++)
 +                {
 +                    if (q == 0)
 +                    {
 +                        tpi = atoms->atom[i].type;
 +                    }
 +                    else
 +                    {
 +                        tpi = atoms->atom[i].typeB;
 +                    }
 +                    j1  = excl->index[i];
 +                    j2  = excl->index[i+1];
 +                    for (j = j1; j < j2; j++)
 +                    {
 +                        k = excl->a[j];
 +                        if (k > i)
 +                        {
 +                            if (q == 0)
 +                            {
 +                                tpj = atoms->atom[k].type;
 +                            }
 +                            else
 +                            {
 +                                tpj = atoms->atom[k].typeB;
 +                            }
 +                            if (bBHAM)
 +                            {
 +                                /* nbfp now includes the 6.0 derivative prefactor */
 +                                csix -= nmol*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 +                            }
 +                            else
 +                            {
 +                                /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                                csix    -= nmol*C6 (nbfp, ntp, tpi, tpj)/6.0;
 +                                ctwelve -= nmol*C12(nbfp, ntp, tpi, tpj)/12.0;
 +                            }
 +                            nexcl += nmol;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            /* Only correct for the interaction of the test particle
 +             * with the rest of the system.
 +             */
 +            atoms_tpi =
 +                &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].atoms;
 +
 +            npair = 0;
 +            for (mb = 0; mb < mtop->nmolblock; mb++)
 +            {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                for (j = 0; j < atoms->nr; j++)
 +                {
 +                    nmolc = nmol;
 +                    /* Remove the interaction of the test charge group
 +                     * with itself.
 +                     */
 +                    if (mb == mtop->nmolblock-1)
 +                    {
 +                        nmolc--;
 +
 +                        if (mb == 0 && nmol == 1)
 +                        {
 +                            gmx_fatal(FARGS, "Old format tpr with TPI, please generate a new tpr file");
 +                        }
 +                    }
 +                    if (q == 0)
 +                    {
 +                        tpj = atoms->atom[j].type;
 +                    }
 +                    else
 +                    {
 +                        tpj = atoms->atom[j].typeB;
 +                    }
 +                    for (i = 0; i < fr->n_tpi; i++)
 +                    {
 +                        if (q == 0)
 +                        {
 +                            tpi = atoms_tpi->atom[i].type;
 +                        }
 +                        else
 +                        {
 +                            tpi = atoms_tpi->atom[i].typeB;
 +                        }
 +                        if (bBHAM)
 +                        {
 +                            /* nbfp now includes the 6.0 derivative prefactor */
 +                            csix    += nmolc*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 +                        }
 +                        else
 +                        {
 +                            /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                            csix    += nmolc*C6 (nbfp, ntp, tpi, tpj)/6.0;
 +                            ctwelve += nmolc*C12(nbfp, ntp, tpi, tpj)/12.0;
 +                        }
 +                        npair += nmolc;
 +                    }
 +                }
 +            }
 +        }
 +        if (npair - nexcl <= 0 && fplog)
 +        {
 +            fprintf(fplog, "\nWARNING: There are no atom pairs for dispersion correction\n\n");
 +            csix     = 0;
 +            ctwelve  = 0;
 +        }
 +        else
 +        {
 +            csix    /= npair - nexcl;
 +            ctwelve /= npair - nexcl;
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Counted %d exclusions\n", nexcl);
 +            fprintf(debug, "Average C6 parameter is: %10g\n", (double)csix);
 +            fprintf(debug, "Average C12 parameter is: %10g\n", (double)ctwelve);
 +        }
 +        fr->avcsix[q]    = csix;
 +        fr->avctwelve[q] = ctwelve;
 +    }
 +
 +    if (EVDW_PME(fr->vdwtype))
 +    {
 +        sfree(nbfp_comb);
 +    }
 +
 +    if (fplog != NULL)
 +    {
 +        if (fr->eDispCorr == edispcAllEner ||
 +            fr->eDispCorr == edispcAllEnerPres)
 +        {
 +            fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
 +                    fr->avcsix[0], fr->avctwelve[0]);
 +        }
 +        else
 +        {
 +            fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e\n", fr->avcsix[0]);
 +        }
 +    }
 +}
 +
 +
 +static void set_bham_b_max(FILE *fplog, t_forcerec *fr,
 +                           const gmx_mtop_t *mtop)
 +{
 +    const t_atoms *at1, *at2;
 +    int            mt1, mt2, i, j, tpi, tpj, ntypes;
 +    real           b, bmin;
 +    real          *nbfp;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Determining largest Buckingham b parameter for table\n");
 +    }
 +    nbfp   = fr->nbfp;
 +    ntypes = fr->ntype;
 +
 +    bmin           = -1;
 +    fr->bham_b_max = 0;
 +    for (mt1 = 0; mt1 < mtop->nmoltype; mt1++)
 +    {
 +        at1 = &mtop->moltype[mt1].atoms;
 +        for (i = 0; (i < at1->nr); i++)
 +        {
 +            tpi = at1->atom[i].type;
 +            if (tpi >= ntypes)
 +            {
 +                gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", i, tpi, ntypes);
 +            }
 +
 +            for (mt2 = mt1; mt2 < mtop->nmoltype; mt2++)
 +            {
 +                at2 = &mtop->moltype[mt2].atoms;
 +                for (j = 0; (j < at2->nr); j++)
 +                {
 +                    tpj = at2->atom[j].type;
 +                    if (tpj >= ntypes)
 +                    {
 +                        gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", j, tpj, ntypes);
 +                    }
 +                    b = BHAMB(nbfp, ntypes, tpi, tpj);
 +                    if (b > fr->bham_b_max)
 +                    {
 +                        fr->bham_b_max = b;
 +                    }
 +                    if ((b < bmin) || (bmin == -1))
 +                    {
 +                        bmin = b;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Buckingham b parameters, min: %g, max: %g\n",
 +                bmin, fr->bham_b_max);
 +    }
 +}
 +
 +static void make_nbf_tables(FILE *fp, const output_env_t oenv,
 +                            t_forcerec *fr, real rtab,
 +                            const t_commrec *cr,
 +                            const char *tabfn, char *eg1, char *eg2,
 +                            t_nblists *nbl)
 +{
 +    char buf[STRLEN];
 +    int  i, j;
 +
 +    if (tabfn == NULL)
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "No table file name passed, can not read table, can not do non-bonded interactions\n");
 +        }
 +        return;
 +    }
 +
 +    sprintf(buf, "%s", tabfn);
 +    if (eg1 && eg2)
 +    {
 +        /* Append the two energy group names */
 +        sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s",
 +                eg1, eg2, ftp2ext(efXVG));
 +    }
 +    nbl->table_elec_vdw = make_tables(fp, oenv, fr, MASTER(cr), buf, rtab, 0);
 +    /* Copy the contents of the table to separate coulomb and LJ tables too,
 +     * to improve cache performance.
 +     */
 +    /* For performance reasons we want
 +     * the table data to be aligned to 16-byte. The pointers could be freed
 +     * but currently aren't.
 +     */
 +    nbl->table_elec.interaction   = GMX_TABLE_INTERACTION_ELEC;
 +    nbl->table_elec.format        = nbl->table_elec_vdw.format;
 +    nbl->table_elec.r             = nbl->table_elec_vdw.r;
 +    nbl->table_elec.n             = nbl->table_elec_vdw.n;
 +    nbl->table_elec.scale         = nbl->table_elec_vdw.scale;
 +    nbl->table_elec.scale_exp     = nbl->table_elec_vdw.scale_exp;
 +    nbl->table_elec.formatsize    = nbl->table_elec_vdw.formatsize;
 +    nbl->table_elec.ninteractions = 1;
 +    nbl->table_elec.stride        = nbl->table_elec.formatsize * nbl->table_elec.ninteractions;
 +    snew_aligned(nbl->table_elec.data, nbl->table_elec.stride*(nbl->table_elec.n+1), 32);
 +
 +    nbl->table_vdw.interaction   = GMX_TABLE_INTERACTION_VDWREP_VDWDISP;
 +    nbl->table_vdw.format        = nbl->table_elec_vdw.format;
 +    nbl->table_vdw.r             = nbl->table_elec_vdw.r;
 +    nbl->table_vdw.n             = nbl->table_elec_vdw.n;
 +    nbl->table_vdw.scale         = nbl->table_elec_vdw.scale;
 +    nbl->table_vdw.scale_exp     = nbl->table_elec_vdw.scale_exp;
 +    nbl->table_vdw.formatsize    = nbl->table_elec_vdw.formatsize;
 +    nbl->table_vdw.ninteractions = 2;
 +    nbl->table_vdw.stride        = nbl->table_vdw.formatsize * nbl->table_vdw.ninteractions;
 +    snew_aligned(nbl->table_vdw.data, nbl->table_vdw.stride*(nbl->table_vdw.n+1), 32);
 +
 +    for (i = 0; i <= nbl->table_elec_vdw.n; i++)
 +    {
 +        for (j = 0; j < 4; j++)
 +        {
 +            nbl->table_elec.data[4*i+j] = nbl->table_elec_vdw.data[12*i+j];
 +        }
 +        for (j = 0; j < 8; j++)
 +        {
 +            nbl->table_vdw.data[8*i+j] = nbl->table_elec_vdw.data[12*i+4+j];
 +        }
 +    }
 +}
 +
 +static void count_tables(int ftype1, int ftype2, const gmx_mtop_t *mtop,
 +                         int *ncount, int **count)
 +{
 +    const gmx_moltype_t *molt;
 +    const t_ilist       *il;
 +    int                  mt, ftype, stride, i, j, tabnr;
 +
 +    for (mt = 0; mt < mtop->nmoltype; mt++)
 +    {
 +        molt = &mtop->moltype[mt];
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (ftype == ftype1 || ftype == ftype2)
 +            {
 +                il     = &molt->ilist[ftype];
 +                stride = 1 + NRAL(ftype);
 +                for (i = 0; i < il->nr; i += stride)
 +                {
 +                    tabnr = mtop->ffparams.iparams[il->iatoms[i]].tab.table;
 +                    if (tabnr < 0)
 +                    {
 +                        gmx_fatal(FARGS, "A bonded table number is smaller than 0: %d\n", tabnr);
 +                    }
 +                    if (tabnr >= *ncount)
 +                    {
 +                        srenew(*count, tabnr+1);
 +                        for (j = *ncount; j < tabnr+1; j++)
 +                        {
 +                            (*count)[j] = 0;
 +                        }
 +                        *ncount = tabnr+1;
 +                    }
 +                    (*count)[tabnr]++;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static bondedtable_t *make_bonded_tables(FILE *fplog,
 +                                         int ftype1, int ftype2,
 +                                         const gmx_mtop_t *mtop,
 +                                         const char *basefn, const char *tabext)
 +{
 +    int            i, ncount, *count;
 +    char           tabfn[STRLEN];
 +    bondedtable_t *tab;
 +
 +    tab = NULL;
 +
 +    ncount = 0;
 +    count  = NULL;
 +    count_tables(ftype1, ftype2, mtop, &ncount, &count);
 +
 +    if (ncount > 0)
 +    {
 +        snew(tab, ncount);
 +        for (i = 0; i < ncount; i++)
 +        {
 +            if (count[i] > 0)
 +            {
 +                sprintf(tabfn, "%s", basefn);
 +                sprintf(tabfn + strlen(basefn) - strlen(ftp2ext(efXVG)) - 1, "_%s%d.%s",
 +                        tabext, i, ftp2ext(efXVG));
 +                tab[i] = make_bonded_table(fplog, tabfn, NRAL(ftype1)-2);
 +            }
 +        }
 +        sfree(count);
 +    }
 +
 +    return tab;
 +}
 +
 +void forcerec_set_ranges(t_forcerec *fr,
 +                         int ncg_home, int ncg_force,
 +                         int natoms_force,
 +                         int natoms_force_constr, int natoms_f_novirsum)
 +{
 +    fr->cg0 = 0;
 +    fr->hcg = ncg_home;
 +
 +    /* fr->ncg_force is unused in the standard code,
 +     * but it can be useful for modified code dealing with charge groups.
 +     */
 +    fr->ncg_force           = ncg_force;
 +    fr->natoms_force        = natoms_force;
 +    fr->natoms_force_constr = natoms_force_constr;
 +
 +    if (fr->natoms_force_constr > fr->nalloc_force)
 +    {
 +        fr->nalloc_force = over_alloc_dd(fr->natoms_force_constr);
 +
 +        if (fr->bTwinRange)
 +        {
 +            srenew(fr->f_twin, fr->nalloc_force);
 +        }
 +    }
 +
 +    if (fr->bF_NoVirSum)
 +    {
 +        fr->f_novirsum_n = natoms_f_novirsum;
 +        if (fr->f_novirsum_n > fr->f_novirsum_nalloc)
 +        {
 +            fr->f_novirsum_nalloc = over_alloc_dd(fr->f_novirsum_n);
 +            srenew(fr->f_novirsum_alloc, fr->f_novirsum_nalloc);
 +        }
 +    }
 +    else
 +    {
 +        fr->f_novirsum_n = 0;
 +    }
 +}
 +
 +static real cutoff_inf(real cutoff)
 +{
 +    if (cutoff == 0)
 +    {
 +        cutoff = GMX_CUTOFF_INF;
 +    }
 +
 +    return cutoff;
 +}
 +
 +static void make_adress_tf_tables(FILE *fp, const output_env_t oenv,
 +                                  t_forcerec *fr, const t_inputrec *ir,
 +                                  const char *tabfn, const gmx_mtop_t *mtop,
 +                                  matrix     box)
 +{
 +    char buf[STRLEN];
 +    int  i, j;
 +
 +    if (tabfn == NULL)
 +    {
 +        gmx_fatal(FARGS, "No thermoforce table file given. Use -tabletf to specify a file\n");
 +        return;
 +    }
 +
 +    snew(fr->atf_tabs, ir->adress->n_tf_grps);
 +
 +    sprintf(buf, "%s", tabfn);
 +    for (i = 0; i < ir->adress->n_tf_grps; i++)
 +    {
 +        j = ir->adress->tf_table_index[i]; /* get energy group index */
 +        sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "tf_%s.%s",
 +                *(mtop->groups.grpname[mtop->groups.grps[egcENER].nm_ind[j]]), ftp2ext(efXVG));
 +        if (fp)
 +        {
 +            fprintf(fp, "loading tf table for energygrp index %d from %s\n", ir->adress->tf_table_index[i], buf);
 +        }
 +        fr->atf_tabs[i] = make_atf_table(fp, oenv, fr, buf, box);
 +    }
 +
 +}
 +
 +gmx_bool can_use_allvsall(const t_inputrec *ir, gmx_bool bPrintNote, t_commrec *cr, FILE *fp)
 +{
 +    gmx_bool bAllvsAll;
 +
 +    bAllvsAll =
 +        (
 +            ir->rlist == 0            &&
 +            ir->rcoulomb == 0         &&
 +            ir->rvdw == 0             &&
 +            ir->ePBC == epbcNONE      &&
 +            ir->vdwtype == evdwCUT    &&
 +            ir->coulombtype == eelCUT &&
 +            ir->efep == efepNO        &&
 +            (ir->implicit_solvent == eisNO ||
 +             (ir->implicit_solvent == eisGBSA && (ir->gb_algorithm == egbSTILL ||
 +                                                  ir->gb_algorithm == egbHCT   ||
 +                                                  ir->gb_algorithm == egbOBC))) &&
 +            getenv("GMX_NO_ALLVSALL") == NULL
 +        );
 +
 +    if (bAllvsAll && ir->opts.ngener > 1)
 +    {
 +        const char *note = "NOTE: Can not use all-vs-all force loops, because there are multiple energy monitor groups; you might get significantly higher performance when using only a single energy monitor group.\n";
 +
 +        if (bPrintNote)
 +        {
 +            if (MASTER(cr))
 +            {
 +                fprintf(stderr, "\n%s\n", note);
 +            }
 +            if (fp != NULL)
 +            {
 +                fprintf(fp, "\n%s\n", note);
 +            }
 +        }
 +        bAllvsAll = FALSE;
 +    }
 +
 +    if (bAllvsAll && fp && MASTER(cr))
 +    {
 +        fprintf(fp, "\nUsing SIMD all-vs-all kernels.\n\n");
 +    }
 +
 +    return bAllvsAll;
 +}
 +
 +
 +static void init_forcerec_f_threads(t_forcerec *fr, int nenergrp)
 +{
 +    int t, i;
 +
 +    /* These thread local data structures are used for bondeds only */
 +    fr->nthreads = gmx_omp_nthreads_get(emntBonded);
 +
 +    if (fr->nthreads > 1)
 +    {
 +        snew(fr->f_t, fr->nthreads);
 +        /* Thread 0 uses the global force and energy arrays */
 +        for (t = 1; t < fr->nthreads; t++)
 +        {
 +            fr->f_t[t].f        = NULL;
 +            fr->f_t[t].f_nalloc = 0;
 +            snew(fr->f_t[t].fshift, SHIFTS);
 +            fr->f_t[t].grpp.nener = nenergrp*nenergrp;
 +            for (i = 0; i < egNR; i++)
 +            {
 +                snew(fr->f_t[t].grpp.ener[i], fr->f_t[t].grpp.nener);
 +            }
 +        }
 +    }
 +}
 +
 +
 +gmx_bool nbnxn_acceleration_supported(FILE             *fplog,
 +                                      const t_commrec  *cr,
 +                                      const t_inputrec *ir,
 +                                      gmx_bool          bGPU)
 +{
 +    if (!bGPU && (ir->vdwtype == evdwPME && ir->ljpme_combination_rule == eljpmeLB))
 +    {
 +        md_print_warn(cr, fplog, "LJ-PME with Lorentz-Berthelot is not supported with %s, falling back to %s\n",
 +                      bGPU ? "GPUs" : "SIMD kernels",
 +                      bGPU ? "CPU only" : "plain-C kernels");
 +        return FALSE;
 +    }
 +
 +    return TRUE;
 +}
 +
 +
 +static void pick_nbnxn_kernel_cpu(const t_inputrec gmx_unused *ir,
 +                                  int                         *kernel_type,
 +                                  int                         *ewald_excl)
 +{
 +    *kernel_type = nbnxnk4x4_PlainC;
 +    *ewald_excl  = ewaldexclTable;
 +
 +#ifdef GMX_NBNXN_SIMD
 +    {
 +#ifdef GMX_NBNXN_SIMD_4XN
 +        *kernel_type = nbnxnk4xN_SIMD_4xN;
 +#endif
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +        *kernel_type = nbnxnk4xN_SIMD_2xNN;
 +#endif
 +
 +#if defined GMX_NBNXN_SIMD_2XNN && defined GMX_NBNXN_SIMD_4XN
 +        /* We need to choose if we want 2x(N+N) or 4xN kernels.
 +         * Currently this is based on the SIMD acceleration choice,
 +         * but it might be better to decide this at runtime based on CPU.
 +         *
 +         * 4xN calculates more (zero) interactions, but has less pair-search
 +         * work and much better kernel instruction scheduling.
 +         *
 +         * Up till now we have only seen that on Intel Sandy/Ivy Bridge,
 +         * which doesn't have FMA, both the analytical and tabulated Ewald
 +         * kernels have similar pair rates for 4x8 and 2x(4+4), so we choose
 +         * 2x(4+4) because it results in significantly fewer pairs.
 +         * For RF, the raw pair rate of the 4x8 kernel is higher than 2x(4+4),
 +         * 10% with HT, 50% without HT. As we currently don't detect the actual
 +         * use of HT, use 4x8 to avoid a potential performance hit.
 +         * On Intel Haswell 4x8 is always faster.
 +         */
 +        *kernel_type = nbnxnk4xN_SIMD_4xN;
 +
 +#ifndef GMX_SIMD_HAVE_FMA
 +        if (EEL_PME_EWALD(ir->coulombtype) ||
 +            EVDW_PME(ir->vdwtype))
 +        {
 +            /* We have Ewald kernels without FMA (Intel Sandy/Ivy Bridge).
 +             * There are enough instructions to make 2x(4+4) efficient.
 +             */
 +            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 +        }
 +#endif
 +#endif  /* GMX_NBNXN_SIMD_2XNN && GMX_NBNXN_SIMD_4XN */
 +
 +
 +        if (getenv("GMX_NBNXN_SIMD_4XN") != NULL)
 +        {
 +#ifdef GMX_NBNXN_SIMD_4XN
 +            *kernel_type = nbnxnk4xN_SIMD_4xN;
 +#else
 +            gmx_fatal(FARGS, "SIMD 4xN kernels requested, but Gromacs has been compiled without support for these kernels");
 +#endif
 +        }
 +        if (getenv("GMX_NBNXN_SIMD_2XNN") != NULL)
 +        {
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 +#else
 +            gmx_fatal(FARGS, "SIMD 2x(N+N) kernels requested, but Gromacs has been compiled without support for these kernels");
 +#endif
 +        }
 +
 +        /* Analytical Ewald exclusion correction is only an option in
 +         * the SIMD kernel.
 +         * Since table lookup's don't parallelize with SIMD, analytical
 +         * will probably always be faster for a SIMD width of 8 or more.
 +         * With FMA analytical is sometimes faster for a width if 4 as well.
 +         * On BlueGene/Q, this is faster regardless of precision.
 +         * In single precision, this is faster on Bulldozer.
 +         */
 +#if GMX_SIMD_REAL_WIDTH >= 8 || \
 +        (GMX_SIMD_REAL_WIDTH >= 4 && defined GMX_SIMD_HAVE_FMA && !defined GMX_DOUBLE) || \
 +        defined GMX_SIMD_IBM_QPX
 +        *ewald_excl = ewaldexclAnalytical;
 +#endif
 +        if (getenv("GMX_NBNXN_EWALD_TABLE") != NULL)
 +        {
 +            *ewald_excl = ewaldexclTable;
 +        }
 +        if (getenv("GMX_NBNXN_EWALD_ANALYTICAL") != NULL)
 +        {
 +            *ewald_excl = ewaldexclAnalytical;
 +        }
 +
 +    }
 +#endif /* GMX_NBNXN_SIMD */
 +}
 +
 +
 +const char *lookup_nbnxn_kernel_name(int kernel_type)
 +{
 +    const char *returnvalue = NULL;
 +    switch (kernel_type)
 +    {
 +        case nbnxnkNotSet:
 +            returnvalue = "not set";
 +            break;
 +        case nbnxnk4x4_PlainC:
 +            returnvalue = "plain C";
 +            break;
 +        case nbnxnk4xN_SIMD_4xN:
 +        case nbnxnk4xN_SIMD_2xNN:
 +#ifdef GMX_NBNXN_SIMD
 +#if defined GMX_SIMD_X86_SSE2
 +            returnvalue = "SSE2";
 +#elif defined GMX_SIMD_X86_SSE4_1
 +            returnvalue = "SSE4.1";
 +#elif defined GMX_SIMD_X86_AVX_128_FMA
 +            returnvalue = "AVX_128_FMA";
 +#elif defined GMX_SIMD_X86_AVX_256
 +            returnvalue = "AVX_256";
 +#elif defined GMX_SIMD_X86_AVX2_256
 +            returnvalue = "AVX2_256";
 +#else
 +            returnvalue = "SIMD";
 +#endif
 +#else  /* GMX_NBNXN_SIMD */
 +            returnvalue = "not available";
 +#endif /* GMX_NBNXN_SIMD */
 +            break;
 +        case nbnxnk8x8x8_CUDA: returnvalue   = "CUDA"; break;
 +        case nbnxnk8x8x8_PlainC: returnvalue = "plain C"; break;
 +
 +        case nbnxnkNR:
 +        default:
 +            gmx_fatal(FARGS, "Illegal kernel type selected");
 +            returnvalue = NULL;
 +            break;
 +    }
 +    return returnvalue;
 +};
 +
 +static void pick_nbnxn_kernel(FILE                *fp,
 +                              const t_commrec     *cr,
 +                              gmx_bool             use_simd_kernels,
 +                              gmx_bool             bUseGPU,
 +                              gmx_bool             bEmulateGPU,
 +                              const t_inputrec    *ir,
 +                              int                 *kernel_type,
 +                              int                 *ewald_excl,
 +                              gmx_bool             bDoNonbonded)
 +{
 +    assert(kernel_type);
 +
 +    *kernel_type = nbnxnkNotSet;
 +    *ewald_excl  = ewaldexclTable;
 +
 +    if (bEmulateGPU)
 +    {
 +        *kernel_type = nbnxnk8x8x8_PlainC;
 +
 +        if (bDoNonbonded)
 +        {
 +            md_print_warn(cr, fp, "Emulating a GPU run on the CPU (slow)");
 +        }
 +    }
 +    else if (bUseGPU)
 +    {
 +        *kernel_type = nbnxnk8x8x8_CUDA;
 +    }
 +
 +    if (*kernel_type == nbnxnkNotSet)
 +    {
 +        /* LJ PME with LB combination rule does 7 mesh operations.
 +         * This so slow that we don't compile SIMD non-bonded kernels for that.
 +         */
 +        if (use_simd_kernels &&
 +            nbnxn_acceleration_supported(fp, cr, ir, FALSE))
 +        {
 +            pick_nbnxn_kernel_cpu(ir, kernel_type, ewald_excl);
 +        }
 +        else
 +        {
 +            *kernel_type = nbnxnk4x4_PlainC;
 +        }
 +    }
 +
 +    if (bDoNonbonded && fp != NULL)
 +    {
 +        fprintf(fp, "\nUsing %s %dx%d non-bonded kernels\n\n",
 +                lookup_nbnxn_kernel_name(*kernel_type),
 +                nbnxn_kernel_pairlist_simple(*kernel_type) ? NBNXN_CPU_CLUSTER_I_SIZE : NBNXN_GPU_CLUSTER_SIZE,
 +                nbnxn_kernel_to_cj_size(*kernel_type));
 +    }
 +}
 +
 +static void pick_nbnxn_resources(const t_commrec     *cr,
 +                                 const gmx_hw_info_t *hwinfo,
 +                                 gmx_bool             bDoNonbonded,
 +                                 gmx_bool            *bUseGPU,
 +                                 gmx_bool            *bEmulateGPU,
 +                                 const gmx_gpu_opt_t *gpu_opt)
 +{
 +    gmx_bool bEmulateGPUEnvVarSet;
 +    char     gpu_err_str[STRLEN];
 +
 +    *bUseGPU = FALSE;
 +
 +    bEmulateGPUEnvVarSet = (getenv("GMX_EMULATE_GPU") != NULL);
 +
 +    /* Run GPU emulation mode if GMX_EMULATE_GPU is defined. Because
 +     * GPUs (currently) only handle non-bonded calculations, we will
 +     * automatically switch to emulation if non-bonded calculations are
 +     * turned off via GMX_NO_NONBONDED - this is the simple and elegant
 +     * way to turn off GPU initialization, data movement, and cleanup.
 +     *
 +     * GPU emulation can be useful to assess the performance one can expect by
 +     * adding GPU(s) to the machine. The conditional below allows this even
 +     * if mdrun is compiled without GPU acceleration support.
 +     * Note that you should freezing the system as otherwise it will explode.
 +     */
 +    *bEmulateGPU = (bEmulateGPUEnvVarSet ||
 +                    (!bDoNonbonded &&
 +                     gpu_opt->ncuda_dev_use > 0));
 +
 +    /* Enable GPU mode when GPUs are available or no GPU emulation is requested.
 +     */
 +    if (gpu_opt->ncuda_dev_use > 0 && !(*bEmulateGPU))
 +    {
 +        /* Each PP node will use the intra-node id-th device from the
 +         * list of detected/selected GPUs. */
 +        if (!init_gpu(cr->rank_pp_intranode, gpu_err_str,
 +                      &hwinfo->gpu_info, gpu_opt))
 +        {
 +            /* At this point the init should never fail as we made sure that
 +             * we have all the GPUs we need. If it still does, we'll bail. */
 +            gmx_fatal(FARGS, "On node %d failed to initialize GPU #%d: %s",
 +                      cr->nodeid,
 +                      get_gpu_device_id(&hwinfo->gpu_info, gpu_opt,
 +                                        cr->rank_pp_intranode),
 +                      gpu_err_str);
 +        }
 +
 +        /* Here we actually turn on hardware GPU acceleration */
 +        *bUseGPU = TRUE;
 +    }
 +}
 +
 +gmx_bool uses_simple_tables(int                 cutoff_scheme,
 +                            nonbonded_verlet_t *nbv,
 +                            int                 group)
 +{
 +    gmx_bool bUsesSimpleTables = TRUE;
 +    int      grp_index;
 +
 +    switch (cutoff_scheme)
 +    {
 +        case ecutsGROUP:
 +            bUsesSimpleTables = TRUE;
 +            break;
 +        case ecutsVERLET:
 +            assert(NULL != nbv && NULL != nbv->grp);
 +            grp_index         = (group < 0) ? 0 : (nbv->ngrp - 1);
 +            bUsesSimpleTables = nbnxn_kernel_pairlist_simple(nbv->grp[grp_index].kernel_type);
 +            break;
 +        default:
 +            gmx_incons("unimplemented");
 +    }
 +    return bUsesSimpleTables;
 +}
 +
 +static void init_ewald_f_table(interaction_const_t *ic,
 +                               gmx_bool             bUsesSimpleTables,
 +                               real                 rtab)
 +{
 +    real maxr;
 +
 +    if (bUsesSimpleTables)
 +    {
 +        /* With a spacing of 0.0005 we are at the force summation accuracy
 +         * for the SSE kernels for "normal" atomistic simulations.
 +         */
 +        ic->tabq_scale = ewald_spline3_table_scale(ic->ewaldcoeff_q,
 +                                                   ic->rcoulomb);
 +
 +        maxr           = (rtab > ic->rcoulomb) ? rtab : ic->rcoulomb;
 +        ic->tabq_size  = (int)(maxr*ic->tabq_scale) + 2;
 +    }
 +    else
 +    {
 +        ic->tabq_size = GPU_EWALD_COULOMB_FORCE_TABLE_SIZE;
 +        /* Subtract 2 iso 1 to avoid access out of range due to rounding */
 +        ic->tabq_scale = (ic->tabq_size - 2)/ic->rcoulomb;
 +    }
 +
 +    sfree_aligned(ic->tabq_coul_FDV0);
 +    sfree_aligned(ic->tabq_coul_F);
 +    sfree_aligned(ic->tabq_coul_V);
 +
 +    sfree_aligned(ic->tabq_vdw_FDV0);
 +    sfree_aligned(ic->tabq_vdw_F);
 +    sfree_aligned(ic->tabq_vdw_V);
 +
 +    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype))
 +    {
 +        /* Create the original table data in FDV0 */
 +        snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32);
 +        snew_aligned(ic->tabq_coul_F, ic->tabq_size, 32);
 +        snew_aligned(ic->tabq_coul_V, ic->tabq_size, 32);
 +        table_spline3_fill_ewald_lr(ic->tabq_coul_F, ic->tabq_coul_V, ic->tabq_coul_FDV0,
 +                                    ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_q, v_q_ewald_lr);
 +    }
 +
 +    if (EVDW_PME(ic->vdwtype))
 +    {
 +        snew_aligned(ic->tabq_vdw_FDV0, ic->tabq_size*4, 32);
 +        snew_aligned(ic->tabq_vdw_F, ic->tabq_size, 32);
 +        snew_aligned(ic->tabq_vdw_V, ic->tabq_size, 32);
 +        table_spline3_fill_ewald_lr(ic->tabq_vdw_F, ic->tabq_vdw_V, ic->tabq_vdw_FDV0,
 +                                    ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_lj, v_lj_ewald_lr);
 +    }
 +}
 +
 +void init_interaction_const_tables(FILE                *fp,
 +                                   interaction_const_t *ic,
 +                                   gmx_bool             bUsesSimpleTables,
 +                                   real                 rtab)
 +{
 +    real spacing;
 +
 +    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype) || EVDW_PME(ic->vdwtype))
 +    {
 +        init_ewald_f_table(ic, bUsesSimpleTables, rtab);
 +
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "Initialized non-bonded Ewald correction tables, spacing: %.2e size: %d\n\n",
 +                    1/ic->tabq_scale, ic->tabq_size);
 +        }
 +    }
 +}
 +
 +static void clear_force_switch_constants(shift_consts_t *sc)
 +{
 +    sc->c2   = 0;
 +    sc->c3   = 0;
 +    sc->cpot = 0;
 +}
 +
 +static void force_switch_constants(real p,
 +                                   real rsw, real rc,
 +                                   shift_consts_t *sc)
 +{
 +    /* Here we determine the coefficient for shifting the force to zero
 +     * between distance rsw and the cut-off rc.
 +     * For a potential of r^-p, we have force p*r^-(p+1).
 +     * But to save flops we absorb p in the coefficient.
 +     * Thus we get:
 +     * force/p   = r^-(p+1) + c2*r^2 + c3*r^3
 +     * potential = r^-p + c2/3*r^3 + c3/4*r^4 + cpot
 +     */
 +    sc->c2   =  ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 2));
 +    sc->c3   = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 3));
 +    sc->cpot = -pow(rc, -p) + p*sc->c2/3*pow(rc - rsw, 3) + p*sc->c3/4*pow(rc - rsw, 4);
 +}
 +
 +static void potential_switch_constants(real rsw, real rc,
 +                                       switch_consts_t *sc)
 +{
 +    /* The switch function is 1 at rsw and 0 at rc.
 +     * The derivative and second derivate are zero at both ends.
 +     * rsw        = max(r - r_switch, 0)
 +     * sw         = 1 + c3*rsw^3 + c4*rsw^4 + c5*rsw^5
 +     * dsw        = 3*c3*rsw^2 + 4*c4*rsw^3 + 5*c5*rsw^4
 +     * force      = force*dsw - potential*sw
 +     * potential *= sw
 +     */
 +    sc->c3 = -10*pow(rc - rsw, -3);
 +    sc->c4 =  15*pow(rc - rsw, -4);
 +    sc->c5 =  -6*pow(rc - rsw, -5);
 +}
 +
 +static void
 +init_interaction_const(FILE                       *fp,
 +                       const t_commrec gmx_unused *cr,
 +                       interaction_const_t       **interaction_const,
 +                       const t_forcerec           *fr,
 +                       real                        rtab)
 +{
 +    interaction_const_t *ic;
 +    gmx_bool             bUsesSimpleTables = TRUE;
 +
 +    snew(ic, 1);
 +
 +    /* Just allocate something so we can free it */
 +    snew_aligned(ic->tabq_coul_FDV0, 16, 32);
 +    snew_aligned(ic->tabq_coul_F, 16, 32);
 +    snew_aligned(ic->tabq_coul_V, 16, 32);
 +
 +    ic->rlist           = fr->rlist;
 +    ic->rlistlong       = fr->rlistlong;
 +
 +    /* Lennard-Jones */
 +    ic->vdwtype         = fr->vdwtype;
 +    ic->vdw_modifier    = fr->vdw_modifier;
 +    ic->rvdw            = fr->rvdw;
 +    ic->rvdw_switch     = fr->rvdw_switch;
 +    ic->ewaldcoeff_lj   = fr->ewaldcoeff_lj;
 +    ic->ljpme_comb_rule = fr->ljpme_combination_rule;
 +    ic->sh_lj_ewald     = 0;
 +    clear_force_switch_constants(&ic->dispersion_shift);
 +    clear_force_switch_constants(&ic->repulsion_shift);
 +
 +    switch (ic->vdw_modifier)
 +    {
 +        case eintmodPOTSHIFT:
 +            /* Only shift the potential, don't touch the force */
 +            ic->dispersion_shift.cpot = -pow(ic->rvdw, -6.0);
 +            ic->repulsion_shift.cpot  = -pow(ic->rvdw, -12.0);
 +            if (EVDW_PME(ic->vdwtype))
 +            {
 +                real crc2;
 +
 +                crc2            = sqr(ic->ewaldcoeff_lj*ic->rvdw);
 +                ic->sh_lj_ewald = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*pow(ic->rvdw, -6.0);
 +            }
 +            break;
 +        case eintmodFORCESWITCH:
 +            /* Switch the force, switch and shift the potential */
 +            force_switch_constants(6.0, ic->rvdw_switch, ic->rvdw,
 +                                   &ic->dispersion_shift);
 +            force_switch_constants(12.0, ic->rvdw_switch, ic->rvdw,
 +                                   &ic->repulsion_shift);
 +            break;
 +        case eintmodPOTSWITCH:
 +            /* Switch the potential and force */
 +            potential_switch_constants(ic->rvdw_switch, ic->rvdw,
 +                                       &ic->vdw_switch);
 +            break;
 +        case eintmodNONE:
 +        case eintmodEXACTCUTOFF:
 +            /* Nothing to do here */
 +            break;
 +        default:
 +            gmx_incons("unimplemented potential modifier");
 +    }
 +
 +    ic->sh_invrc6 = -ic->dispersion_shift.cpot;
 +
 +    /* Electrostatics */
 +    ic->eeltype          = fr->eeltype;
 +    ic->coulomb_modifier = fr->coulomb_modifier;
 +    ic->rcoulomb         = fr->rcoulomb;
 +    ic->epsilon_r        = fr->epsilon_r;
 +    ic->epsfac           = fr->epsfac;
 +    ic->ewaldcoeff_q     = fr->ewaldcoeff_q;
 +
 +    if (fr->coulomb_modifier == eintmodPOTSHIFT)
 +    {
 +        ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb);
 +    }
 +    else
 +    {
 +        ic->sh_ewald = 0;
 +    }
 +
 +    /* Reaction-field */
 +    if (EEL_RF(ic->eeltype))
 +    {
 +        ic->epsilon_rf = fr->epsilon_rf;
 +        ic->k_rf       = fr->k_rf;
 +        ic->c_rf       = fr->c_rf;
 +    }
 +    else
 +    {
 +        /* For plain cut-off we might use the reaction-field kernels */
 +        ic->epsilon_rf = ic->epsilon_r;
 +        ic->k_rf       = 0;
 +        if (fr->coulomb_modifier == eintmodPOTSHIFT)
 +        {
 +            ic->c_rf   = 1/ic->rcoulomb;
 +        }
 +        else
 +        {
 +            ic->c_rf   = 0;
 +        }
 +    }
 +
 +    if (fp != NULL)
 +    {
 +        real dispersion_shift;
 +
 +        dispersion_shift = ic->dispersion_shift.cpot;
 +        if (EVDW_PME(ic->vdwtype))
 +        {
 +            dispersion_shift -= ic->sh_lj_ewald;
 +        }
 +        fprintf(fp, "Potential shift: LJ r^-12: %.3e r^-6: %.3e",
 +                ic->repulsion_shift.cpot, dispersion_shift);
 +
 +        if (ic->eeltype == eelCUT)
 +        {
 +            fprintf(fp, ", Coulomb %.e", -ic->c_rf);
 +        }
 +        else if (EEL_PME(ic->eeltype))
 +        {
 +            fprintf(fp, ", Ewald %.3e", -ic->sh_ewald);
 +        }
 +        fprintf(fp, "\n");
 +    }
 +
 +    *interaction_const = ic;
 +
 +    if (fr->nbv != NULL && fr->nbv->bUseGPU)
 +    {
 +        nbnxn_cuda_init_const(fr->nbv->cu_nbv, ic, fr->nbv->grp);
 +
 +        /* With tMPI + GPUs some ranks may be sharing GPU(s) and therefore
 +         * also sharing texture references. To keep the code simple, we don't
 +         * treat texture references as shared resources, but this means that
 +         * the coulomb_tab and nbfp texture refs will get updated by multiple threads.
 +         * Hence, to ensure that the non-bonded kernels don't start before all
 +         * texture binding operations are finished, we need to wait for all ranks
 +         * to arrive here before continuing.
 +         *
 +         * Note that we could omit this barrier if GPUs are not shared (or
 +         * texture objects are used), but as this is initialization code, there
 +         * is not point in complicating things.
 +         */
 +#ifdef GMX_THREAD_MPI
 +        if (PAR(cr))
 +        {
 +            gmx_barrier(cr);
 +        }
 +#endif  /* GMX_THREAD_MPI */
 +    }
 +
 +    bUsesSimpleTables = uses_simple_tables(fr->cutoff_scheme, fr->nbv, -1);
 +    init_interaction_const_tables(fp, ic, bUsesSimpleTables, rtab);
 +}
 +
 +static void init_nb_verlet(FILE                *fp,
 +                           nonbonded_verlet_t **nb_verlet,
 +                           gmx_bool             bFEP_NonBonded,
 +                           const t_inputrec    *ir,
 +                           const t_forcerec    *fr,
 +                           const t_commrec     *cr,
 +                           const char          *nbpu_opt)
 +{
 +    nonbonded_verlet_t *nbv;
 +    int                 i;
 +    char               *env;
 +    gmx_bool            bEmulateGPU, bHybridGPURun = FALSE;
 +
 +    nbnxn_alloc_t      *nb_alloc;
 +    nbnxn_free_t       *nb_free;
 +
 +    snew(nbv, 1);
 +
 +    pick_nbnxn_resources(cr, fr->hwinfo,
 +                         fr->bNonbonded,
 +                         &nbv->bUseGPU,
 +                         &bEmulateGPU,
 +                         fr->gpu_opt);
 +
 +    nbv->nbs = NULL;
 +
 +    nbv->ngrp = (DOMAINDECOMP(cr) ? 2 : 1);
 +    for (i = 0; i < nbv->ngrp; i++)
 +    {
 +        nbv->grp[i].nbl_lists.nnbl = 0;
 +        nbv->grp[i].nbat           = NULL;
 +        nbv->grp[i].kernel_type    = nbnxnkNotSet;
 +
 +        if (i == 0) /* local */
 +        {
 +            pick_nbnxn_kernel(fp, cr, fr->use_simd_kernels,
 +                              nbv->bUseGPU, bEmulateGPU, ir,
 +                              &nbv->grp[i].kernel_type,
 +                              &nbv->grp[i].ewald_excl,
 +                              fr->bNonbonded);
 +        }
 +        else /* non-local */
 +        {
 +            if (nbpu_opt != NULL && strcmp(nbpu_opt, "gpu_cpu") == 0)
 +            {
 +                /* Use GPU for local, select a CPU kernel for non-local */
 +                pick_nbnxn_kernel(fp, cr, fr->use_simd_kernels,
 +                                  FALSE, FALSE, ir,
 +                                  &nbv->grp[i].kernel_type,
 +                                  &nbv->grp[i].ewald_excl,
 +                                  fr->bNonbonded);
 +
 +                bHybridGPURun = TRUE;
 +            }
 +            else
 +            {
 +                /* Use the same kernel for local and non-local interactions */
 +                nbv->grp[i].kernel_type = nbv->grp[0].kernel_type;
 +                nbv->grp[i].ewald_excl  = nbv->grp[0].ewald_excl;
 +            }
 +        }
 +    }
 +
 +    if (nbv->bUseGPU)
 +    {
 +        /* init the NxN GPU data; the last argument tells whether we'll have
 +         * both local and non-local NB calculation on GPU */
 +        nbnxn_cuda_init(fp, &nbv->cu_nbv,
 +                        &fr->hwinfo->gpu_info, fr->gpu_opt,
 +                        cr->rank_pp_intranode,
 +                        (nbv->ngrp > 1) && !bHybridGPURun);
 +
 +        if ((env = getenv("GMX_NB_MIN_CI")) != NULL)
 +        {
 +            char *end;
 +
 +            nbv->min_ci_balanced = strtol(env, &end, 10);
 +            if (!end || (*end != 0) || nbv->min_ci_balanced <= 0)
 +            {
 +                gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, positive integer required", env);
 +            }
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "Neighbor-list balancing parameter: %d (passed as env. var.)\n",
 +                        nbv->min_ci_balanced);
 +            }
 +        }
 +        else
 +        {
 +            nbv->min_ci_balanced = nbnxn_cuda_min_ci_balanced(nbv->cu_nbv);
 +            if (debug)
 +            {
 +                fprintf(debug, "Neighbor-list balancing parameter: %d (auto-adjusted to the number of GPU multi-processors)\n",
 +                        nbv->min_ci_balanced);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        nbv->min_ci_balanced = 0;
 +    }
 +
 +    *nb_verlet = nbv;
 +
 +    nbnxn_init_search(&nbv->nbs,
 +                      DOMAINDECOMP(cr) ? &cr->dd->nc : NULL,
 +                      DOMAINDECOMP(cr) ? domdec_zones(cr->dd) : NULL,
 +                      bFEP_NonBonded,
 +                      gmx_omp_nthreads_get(emntNonbonded));
 +
 +    for (i = 0; i < nbv->ngrp; i++)
 +    {
 +        if (nbv->grp[0].kernel_type == nbnxnk8x8x8_CUDA)
 +        {
 +            nb_alloc = &pmalloc;
 +            nb_free  = &pfree;
 +        }
 +        else
 +        {
 +            nb_alloc = NULL;
 +            nb_free  = NULL;
 +        }
 +
 +        nbnxn_init_pairlist_set(&nbv->grp[i].nbl_lists,
 +                                nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type),
 +                                /* 8x8x8 "non-simple" lists are ATM always combined */
 +                                !nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type),
 +                                nb_alloc, nb_free);
 +
 +        if (i == 0 ||
 +            nbv->grp[0].kernel_type != nbv->grp[i].kernel_type)
 +        {
 +            gmx_bool bSimpleList;
 +            int      enbnxninitcombrule;
 +
 +            bSimpleList = nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type);
 +
 +            if (bSimpleList && (fr->vdwtype == evdwCUT && (fr->vdw_modifier == eintmodNONE || fr->vdw_modifier == eintmodPOTSHIFT)))
 +            {
 +                /* Plain LJ cut-off: we can optimize with combination rules */
 +                enbnxninitcombrule = enbnxninitcombruleDETECT;
 +            }
 +            else if (fr->vdwtype == evdwPME)
 +            {
 +                /* LJ-PME: we need to use a combination rule for the grid */
 +                if (fr->ljpme_combination_rule == eljpmeGEOM)
 +                {
 +                    enbnxninitcombrule = enbnxninitcombruleGEOM;
 +                }
 +                else
 +                {
 +                    enbnxninitcombrule = enbnxninitcombruleLB;
 +                }
 +            }
 +            else
 +            {
 +                /* We use a full combination matrix: no rule required */
 +                enbnxninitcombrule = enbnxninitcombruleNONE;
 +            }
 +
 +
 +            snew(nbv->grp[i].nbat, 1);
 +            nbnxn_atomdata_init(fp,
 +                                nbv->grp[i].nbat,
 +                                nbv->grp[i].kernel_type,
 +                                enbnxninitcombrule,
 +                                fr->ntype, fr->nbfp,
 +                                ir->opts.ngener,
 +                                bSimpleList ? gmx_omp_nthreads_get(emntNonbonded) : 1,
 +                                nb_alloc, nb_free);
 +        }
 +        else
 +        {
 +            nbv->grp[i].nbat = nbv->grp[0].nbat;
 +        }
 +    }
 +}
 +
 +void init_forcerec(FILE              *fp,
 +                   const output_env_t oenv,
 +                   t_forcerec        *fr,
 +                   t_fcdata          *fcd,
 +                   const t_inputrec  *ir,
 +                   const gmx_mtop_t  *mtop,
 +                   const t_commrec   *cr,
 +                   matrix             box,
 +                   const char        *tabfn,
 +                   const char        *tabafn,
 +                   const char        *tabpfn,
 +                   const char        *tabbfn,
 +                   const char        *nbpu_opt,
 +                   gmx_bool           bNoSolvOpt,
 +                   real               print_force)
 +{
 +    int            i, j, m, natoms, ngrp, negp_pp, negptable, egi, egj;
 +    real           rtab;
 +    char          *env;
 +    double         dbl;
 +    const t_block *cgs;
 +    gmx_bool       bGenericKernelOnly;
 +    gmx_bool       bMakeTables, bMakeSeparate14Table, bSomeNormalNbListsAreInUse;
 +    gmx_bool       bFEP_NonBonded;
 +    t_nblists     *nbl;
 +    int           *nm_ind, egp_flags;
 +
 +    if (fr->hwinfo == NULL)
 +    {
 +        /* Detect hardware, gather information.
 +         * In mdrun, hwinfo has already been set before calling init_forcerec.
 +         * Here we ignore GPUs, as tools will not use them anyhow.
 +         */
 +        fr->hwinfo = gmx_detect_hardware(fp, cr, FALSE);
 +    }
 +
 +    /* By default we turn SIMD kernels on, but it might be turned off further down... */
 +    fr->use_simd_kernels = TRUE;
 +
 +    fr->bDomDec = DOMAINDECOMP(cr);
 +
 +    natoms = mtop->natoms;
 +
 +    if (check_box(ir->ePBC, box))
 +    {
 +        gmx_fatal(FARGS, check_box(ir->ePBC, box));
 +    }
 +
 +    /* Test particle insertion ? */
 +    if (EI_TPI(ir->eI))
 +    {
 +        /* Set to the size of the molecule to be inserted (the last one) */
 +        /* Because of old style topologies, we have to use the last cg
 +         * instead of the last molecule type.
 +         */
 +        cgs       = &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].cgs;
 +        fr->n_tpi = cgs->index[cgs->nr] - cgs->index[cgs->nr-1];
 +        if (fr->n_tpi != mtop->mols.index[mtop->mols.nr] - mtop->mols.index[mtop->mols.nr-1])
 +        {
 +            gmx_fatal(FARGS, "The molecule to insert can not consist of multiple charge groups.\nMake it a single charge group.");
 +        }
 +    }
 +    else
 +    {
 +        fr->n_tpi = 0;
 +    }
 +
 +    /* Copy AdResS parameters */
 +    if (ir->bAdress)
 +    {
 +        fr->adress_type           = ir->adress->type;
 +        fr->adress_const_wf       = ir->adress->const_wf;
 +        fr->adress_ex_width       = ir->adress->ex_width;
 +        fr->adress_hy_width       = ir->adress->hy_width;
 +        fr->adress_icor           = ir->adress->icor;
 +        fr->adress_site           = ir->adress->site;
 +        fr->adress_ex_forcecap    = ir->adress->ex_forcecap;
 +        fr->adress_do_hybridpairs = ir->adress->do_hybridpairs;
 +
 +
 +        snew(fr->adress_group_explicit, ir->adress->n_energy_grps);
 +        for (i = 0; i < ir->adress->n_energy_grps; i++)
 +        {
 +            fr->adress_group_explicit[i] = ir->adress->group_explicit[i];
 +        }
 +
 +        fr->n_adress_tf_grps = ir->adress->n_tf_grps;
 +        snew(fr->adress_tf_table_index, fr->n_adress_tf_grps);
 +        for (i = 0; i < fr->n_adress_tf_grps; i++)
 +        {
 +            fr->adress_tf_table_index[i] = ir->adress->tf_table_index[i];
 +        }
 +        copy_rvec(ir->adress->refs, fr->adress_refs);
 +    }
 +    else
 +    {
 +        fr->adress_type           = eAdressOff;
 +        fr->adress_do_hybridpairs = FALSE;
 +    }
 +
 +    /* Copy the user determined parameters */
 +    fr->userint1  = ir->userint1;
 +    fr->userint2  = ir->userint2;
 +    fr->userint3  = ir->userint3;
 +    fr->userint4  = ir->userint4;
 +    fr->userreal1 = ir->userreal1;
 +    fr->userreal2 = ir->userreal2;
 +    fr->userreal3 = ir->userreal3;
 +    fr->userreal4 = ir->userreal4;
 +
 +    /* Shell stuff */
 +    fr->fc_stepsize = ir->fc_stepsize;
 +
 +    /* Free energy */
 +    fr->efep        = ir->efep;
 +    fr->sc_alphavdw = ir->fepvals->sc_alpha;
 +    if (ir->fepvals->bScCoul)
 +    {
 +        fr->sc_alphacoul  = ir->fepvals->sc_alpha;
 +        fr->sc_sigma6_min = pow(ir->fepvals->sc_sigma_min, 6);
 +    }
 +    else
 +    {
 +        fr->sc_alphacoul  = 0;
 +        fr->sc_sigma6_min = 0; /* only needed when bScCoul is on */
 +    }
 +    fr->sc_power      = ir->fepvals->sc_power;
 +    fr->sc_r_power    = ir->fepvals->sc_r_power;
 +    fr->sc_sigma6_def = pow(ir->fepvals->sc_sigma, 6);
 +
 +    env = getenv("GMX_SCSIGMA_MIN");
 +    if (env != NULL)
 +    {
 +        dbl = 0;
 +        sscanf(env, "%lf", &dbl);
 +        fr->sc_sigma6_min = pow(dbl, 6);
 +        if (fp)
 +        {
 +            fprintf(fp, "Setting the minimum soft core sigma to %g nm\n", dbl);
 +        }
 +    }
 +
 +    fr->bNonbonded = TRUE;
 +    if (getenv("GMX_NO_NONBONDED") != NULL)
 +    {
 +        /* turn off non-bonded calculations */
 +        fr->bNonbonded = FALSE;
 +        md_print_warn(cr, fp,
 +                      "Found environment variable GMX_NO_NONBONDED.\n"
 +                      "Disabling nonbonded calculations.\n");
 +    }
 +
 +    bGenericKernelOnly = FALSE;
 +
 +    /* We now check in the NS code whether a particular combination of interactions
 +     * can be used with water optimization, and disable it if that is not the case.
 +     */
 +
 +    if (getenv("GMX_NB_GENERIC") != NULL)
 +    {
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "Found environment variable GMX_NB_GENERIC.\n"
 +                    "Disabling all interaction-specific nonbonded kernels, will only\n"
 +                    "use the slow generic ones in src/gmxlib/nonbonded/nb_generic.c\n\n");
 +        }
 +        bGenericKernelOnly = TRUE;
 +    }
 +
 +    if (bGenericKernelOnly == TRUE)
 +    {
 +        bNoSolvOpt         = TRUE;
 +    }
 +
 +    if ( (getenv("GMX_DISABLE_SIMD_KERNELS") != NULL) || (getenv("GMX_NOOPTIMIZEDKERNELS") != NULL) )
 +    {
 +        fr->use_simd_kernels = FALSE;
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "\nFound environment variable GMX_DISABLE_SIMD_KERNELS.\n"
 +                    "Disabling the usage of any SIMD-specific kernel routines (e.g. SSE2/SSE4.1/AVX).\n\n");
 +        }
 +    }
 +
 +    fr->bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
 +
 +    /* Check if we can/should do all-vs-all kernels */
 +    fr->bAllvsAll       = can_use_allvsall(ir, FALSE, NULL, NULL);
 +    fr->AllvsAll_work   = NULL;
 +    fr->AllvsAll_workgb = NULL;
 +
 +    /* All-vs-all kernels have not been implemented in 4.6, and
 +     * the SIMD group kernels are also buggy in this case. Non-SIMD
 +     * group kernels are OK. See Redmine #1249. */
 +    if (fr->bAllvsAll)
 +    {
 +        fr->bAllvsAll            = FALSE;
 +        fr->use_simd_kernels     = FALSE;
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "\nYour simulation settings would have triggered the efficient all-vs-all\n"
 +                    "kernels in GROMACS 4.5, but these have not been implemented in GROMACS\n"
 +                    "4.6. Also, we can't use the accelerated SIMD kernels here because\n"
 +                    "of an unfixed bug. The reference C kernels are correct, though, so\n"
 +                    "we are proceeding by disabling all CPU architecture-specific\n"
 +                    "(e.g. SSE2/SSE4/AVX) routines. If performance is important, please\n"
 +                    "use GROMACS 4.5.7 or try cutoff-scheme = Verlet.\n\n");
 +        }
 +    }
 +
 +    /* Neighbour searching stuff */
 +    fr->cutoff_scheme = ir->cutoff_scheme;
 +    fr->bGrid         = (ir->ns_type == ensGRID);
 +    fr->ePBC          = ir->ePBC;
 +
 +    if (fr->cutoff_scheme == ecutsGROUP)
 +    {
 +        const char *note = "NOTE: This file uses the deprecated 'group' cutoff_scheme. This will be\n"
 +            "removed in a future release when 'verlet' supports all interaction forms.\n";
 +
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, "\n%s\n", note);
 +        }
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "\n%s\n", note);
 +        }
 +    }
 +
 +    /* Determine if we will do PBC for distances in bonded interactions */
 +    if (fr->ePBC == epbcNONE)
 +    {
 +        fr->bMolPBC = FALSE;
 +    }
 +    else
 +    {
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            /* The group cut-off scheme and SHAKE assume charge groups
 +             * are whole, but not using molpbc is faster in most cases.
 +             */
 +            if (fr->cutoff_scheme == ecutsGROUP ||
 +                (ir->eConstrAlg == econtSHAKE &&
 +                 (gmx_mtop_ftype_count(mtop, F_CONSTR) > 0 ||
 +                  gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0)))
 +            {
 +                fr->bMolPBC = ir->bPeriodicMols;
 +            }
 +            else
 +            {
 +                fr->bMolPBC = TRUE;
 +                if (getenv("GMX_USE_GRAPH") != NULL)
 +                {
 +                    fr->bMolPBC = FALSE;
 +                    if (fp)
 +                    {
 +                        fprintf(fp, "\nGMX_MOLPBC is set, using the graph for bonded interactions\n\n");
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            fr->bMolPBC = dd_bonded_molpbc(cr->dd, fr->ePBC);
 +        }
 +    }
 +    fr->bGB = (ir->implicit_solvent == eisGBSA);
 +
 +    fr->rc_scaling = ir->refcoord_scaling;
 +    copy_rvec(ir->posres_com, fr->posres_com);
 +    copy_rvec(ir->posres_comB, fr->posres_comB);
 +    fr->rlist                    = cutoff_inf(ir->rlist);
 +    fr->rlistlong                = cutoff_inf(ir->rlistlong);
 +    fr->eeltype                  = ir->coulombtype;
 +    fr->vdwtype                  = ir->vdwtype;
 +    fr->ljpme_combination_rule   = ir->ljpme_combination_rule;
 +
 +    fr->coulomb_modifier = ir->coulomb_modifier;
 +    fr->vdw_modifier     = ir->vdw_modifier;
 +
 +    /* Electrostatics: Translate from interaction-setting-in-mdp-file to kernel interaction format */
 +    switch (fr->eeltype)
 +    {
 +        case eelCUT:
 +            fr->nbkernel_elec_interaction = (fr->bGB) ? GMX_NBKERNEL_ELEC_GENERALIZEDBORN : GMX_NBKERNEL_ELEC_COULOMB;
 +            break;
 +
 +        case eelRF:
 +        case eelGRF:
 +        case eelRF_NEC:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
 +            break;
 +
 +        case eelRF_ZERO:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
 +            fr->coulomb_modifier          = eintmodEXACTCUTOFF;
 +            break;
 +
 +        case eelSWITCH:
 +        case eelSHIFT:
 +        case eelUSER:
 +        case eelENCADSHIFT:
 +        case eelPMESWITCH:
 +        case eelPMEUSER:
 +        case eelPMEUSERSWITCH:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE;
 +            break;
 +
 +        case eelPME:
 +        case eelEWALD:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_EWALD;
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Unsupported electrostatic interaction: %s", eel_names[fr->eeltype]);
 +            break;
 +    }
 +
 +    /* Vdw: Translate from mdp settings to kernel format */
 +    switch (fr->vdwtype)
 +    {
 +        case evdwCUT:
 +            if (fr->bBHAM)
 +            {
 +                fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_BUCKINGHAM;
 +            }
 +            else
 +            {
 +                fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LENNARDJONES;
 +            }
 +            break;
 +        case evdwPME:
 +            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LJEWALD;
 +            break;
 +
 +        case evdwSWITCH:
 +        case evdwSHIFT:
 +        case evdwUSER:
 +        case evdwENCADSHIFT:
 +            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Unsupported vdw interaction: %s", evdw_names[fr->vdwtype]);
 +            break;
 +    }
 +
 +    /* These start out identical to ir, but might be altered if we e.g. tabulate the interaction in the kernel */
 +    fr->nbkernel_elec_modifier    = fr->coulomb_modifier;
 +    fr->nbkernel_vdw_modifier     = fr->vdw_modifier;
 +
 +    fr->bTwinRange = fr->rlistlong > fr->rlist;
 +    fr->bEwald     = (EEL_PME(fr->eeltype) || fr->eeltype == eelEWALD);
 +
 +    fr->reppow     = mtop->ffparams.reppow;
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        fr->bvdwtab    = ((fr->vdwtype != evdwCUT || !gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +                          && !EVDW_PME(fr->vdwtype));
 +        /* We have special kernels for standard Ewald and PME, but the pme-switch ones are tabulated above */
 +        fr->bcoultab   = !(fr->eeltype == eelCUT ||
 +                           fr->eeltype == eelEWALD ||
 +                           fr->eeltype == eelPME ||
 +                           fr->eeltype == eelRF ||
 +                           fr->eeltype == eelRF_ZERO);
 +
 +        /* If the user absolutely wants different switch/shift settings for coul/vdw, it is likely
 +         * going to be faster to tabulate the interaction than calling the generic kernel.
 +         */
 +        if (fr->nbkernel_elec_modifier == eintmodPOTSWITCH && fr->nbkernel_vdw_modifier == eintmodPOTSWITCH)
 +        {
 +            if ((fr->rcoulomb_switch != fr->rvdw_switch) || (fr->rcoulomb != fr->rvdw))
 +            {
 +                fr->bcoultab = TRUE;
 +            }
 +        }
 +        else if ((fr->nbkernel_elec_modifier == eintmodPOTSHIFT && fr->nbkernel_vdw_modifier == eintmodPOTSHIFT) ||
 +                 ((fr->nbkernel_elec_interaction == GMX_NBKERNEL_ELEC_REACTIONFIELD &&
 +                   fr->nbkernel_elec_modifier == eintmodEXACTCUTOFF &&
 +                   (fr->nbkernel_vdw_modifier == eintmodPOTSWITCH || fr->nbkernel_vdw_modifier == eintmodPOTSHIFT))))
 +        {
 +            if (fr->rcoulomb != fr->rvdw)
 +            {
 +                fr->bcoultab = TRUE;
 +            }
 +        }
 +
 +        if (getenv("GMX_REQUIRE_TABLES"))
 +        {
 +            fr->bvdwtab  = TRUE;
 +            fr->bcoultab = TRUE;
 +        }
 +
 +        if (fp)
 +        {
 +            fprintf(fp, "Table routines are used for coulomb: %s\n", bool_names[fr->bcoultab]);
 +            fprintf(fp, "Table routines are used for vdw:     %s\n", bool_names[fr->bvdwtab ]);
 +        }
 +
 +        if (fr->bvdwtab == TRUE)
 +        {
 +            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
 +            fr->nbkernel_vdw_modifier    = eintmodNONE;
 +        }
 +        if (fr->bcoultab == TRUE)
 +        {
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE;
 +            fr->nbkernel_elec_modifier    = eintmodNONE;
 +        }
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        if (!gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        {
 +            gmx_fatal(FARGS, "Cut-off scheme %S only supports LJ repulsion power 12", ecutscheme_names[ir->cutoff_scheme]);
 +        }
 +        fr->bvdwtab  = FALSE;
 +        fr->bcoultab = FALSE;
 +    }
 +
 +    /* Tables are used for direct ewald sum */
 +    if (fr->bEwald)
 +    {
 +        if (EEL_PME(ir->coulombtype))
 +        {
 +            if (fp)
 +            {
 +                fprintf(fp, "Will do PME sum in reciprocal space for electrostatic interactions.\n");
 +            }
 +            if (ir->coulombtype == eelP3M_AD)
 +            {
 +                please_cite(fp, "Hockney1988");
 +                please_cite(fp, "Ballenegger2012");
 +            }
 +            else
 +            {
 +                please_cite(fp, "Essmann95a");
 +            }
 +
 +            if (ir->ewald_geometry == eewg3DC)
 +            {
 +                if (fp)
 +                {
 +                    fprintf(fp, "Using the Ewald3DC correction for systems with a slab geometry.\n");
 +                }
 +                please_cite(fp, "In-Chul99a");
 +            }
 +        }
 +        fr->ewaldcoeff_q = calc_ewaldcoeff_q(ir->rcoulomb, ir->ewald_rtol);
 +        init_ewald_tab(&(fr->ewald_table), ir, fp);
 +        if (fp)
 +        {
 +            fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n",
 +                    1/fr->ewaldcoeff_q);
 +        }
 +    }
 +
 +    if (EVDW_PME(ir->vdwtype))
 +    {
 +        if (fp)
 +        {
 +            fprintf(fp, "Will do PME sum in reciprocal space for LJ dispersion interactions.\n");
 +        }
 +        please_cite(fp, "Essmann95a");
 +        fr->ewaldcoeff_lj = calc_ewaldcoeff_lj(ir->rvdw, ir->ewald_rtol_lj);
 +        if (fp)
 +        {
 +            fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for LJ Ewald\n",
 +                    1/fr->ewaldcoeff_lj);
 +        }
 +    }
 +
 +    /* Electrostatics */
 +    fr->epsilon_r       = ir->epsilon_r;
 +    fr->epsilon_rf      = ir->epsilon_rf;
 +    fr->fudgeQQ         = mtop->ffparams.fudgeQQ;
 +    fr->rcoulomb_switch = ir->rcoulomb_switch;
 +    fr->rcoulomb        = cutoff_inf(ir->rcoulomb);
 +
 +    /* Parameters for generalized RF */
 +    fr->zsquare = 0.0;
 +    fr->temp    = 0.0;
 +
 +    if (fr->eeltype == eelGRF)
 +    {
 +        init_generalized_rf(fp, mtop, ir, fr);
 +    }
 +
 +    fr->bF_NoVirSum = (EEL_FULL(fr->eeltype) || EVDW_PME(fr->vdwtype) ||
 +                       gmx_mtop_ftype_count(mtop, F_POSRES) > 0 ||
 +                       gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0 ||
 +                       IR_ELEC_FIELD(*ir) ||
 +                       (fr->adress_icor != eAdressICOff)
 +                       );
 +
 +    if (fr->cutoff_scheme == ecutsGROUP &&
 +        ncg_mtop(mtop) > fr->cg_nalloc && !DOMAINDECOMP(cr))
 +    {
 +        /* Count the total number of charge groups */
 +        fr->cg_nalloc = ncg_mtop(mtop);
 +        srenew(fr->cg_cm, fr->cg_nalloc);
 +    }
 +    if (fr->shift_vec == NULL)
 +    {
 +        snew(fr->shift_vec, SHIFTS);
 +    }
 +
 +    if (fr->fshift == NULL)
 +    {
 +        snew(fr->fshift, SHIFTS);
 +    }
 +
 +    if (fr->nbfp == NULL)
 +    {
 +        fr->ntype = mtop->ffparams.atnr;
 +        fr->nbfp  = mk_nbfp(&mtop->ffparams, fr->bBHAM);
 +        if (EVDW_PME(fr->vdwtype))
 +        {
 +            fr->ljpme_c6grid  = make_ljpme_c6grid(&mtop->ffparams, fr);
 +        }
 +    }
 +
 +    /* Copy the energy group exclusions */
 +    fr->egp_flags = ir->opts.egp_flags;
 +
 +    /* Van der Waals stuff */
 +    fr->rvdw        = cutoff_inf(ir->rvdw);
 +    fr->rvdw_switch = ir->rvdw_switch;
 +    if ((fr->vdwtype != evdwCUT) && (fr->vdwtype != evdwUSER) && !fr->bBHAM)
 +    {
 +        if (fr->rvdw_switch >= fr->rvdw)
 +        {
 +            gmx_fatal(FARGS, "rvdw_switch (%f) must be < rvdw (%f)",
 +                      fr->rvdw_switch, fr->rvdw);
 +        }
 +        if (fp)
 +        {
 +            fprintf(fp, "Using %s Lennard-Jones, switch between %g and %g nm\n",
 +                    (fr->eeltype == eelSWITCH) ? "switched" : "shifted",
 +                    fr->rvdw_switch, fr->rvdw);
 +        }
 +    }
 +
 +    if (fr->bBHAM && EVDW_PME(fr->vdwtype))
 +    {
 +        gmx_fatal(FARGS, "LJ PME not supported with Buckingham");
 +    }
 +
 +    if (fr->bBHAM && (fr->vdwtype == evdwSHIFT || fr->vdwtype == evdwSWITCH))
 +    {
 +        gmx_fatal(FARGS, "Switch/shift interaction not supported with Buckingham");
++    }
++
++    if (fr->bBHAM && fr->cutoff_scheme == ecutsVERLET)
++    {
++        gmx_fatal(FARGS, "Verlet cutoff-scheme is not supported with Buckingham");
 +    }
 +
 +    if (fp)
 +    {
 +        fprintf(fp, "Cut-off's:   NS: %g   Coulomb: %g   %s: %g\n",
 +                fr->rlist, fr->rcoulomb, fr->bBHAM ? "BHAM" : "LJ", fr->rvdw);
 +    }
 +
 +    fr->eDispCorr = ir->eDispCorr;
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        set_avcsixtwelve(fp, fr, mtop);
 +    }
 +
 +    if (fr->bBHAM)
 +    {
 +        set_bham_b_max(fp, fr, mtop);
 +    }
 +
 +    fr->gb_epsilon_solvent = ir->gb_epsilon_solvent;
 +
 +    /* Copy the GBSA data (radius, volume and surftens for each
 +     * atomtype) from the topology atomtype section to forcerec.
 +     */
 +    snew(fr->atype_radius, fr->ntype);
 +    snew(fr->atype_vol, fr->ntype);
 +    snew(fr->atype_surftens, fr->ntype);
 +    snew(fr->atype_gb_radius, fr->ntype);
 +    snew(fr->atype_S_hct, fr->ntype);
 +
 +    if (mtop->atomtypes.nr > 0)
 +    {
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_radius[i] = mtop->atomtypes.radius[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_vol[i] = mtop->atomtypes.vol[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_surftens[i] = mtop->atomtypes.surftens[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_gb_radius[i] = mtop->atomtypes.gb_radius[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_S_hct[i] = mtop->atomtypes.S_hct[i];
 +        }
 +    }
 +
 +    /* Generate the GB table if needed */
 +    if (fr->bGB)
 +    {
 +#ifdef GMX_DOUBLE
 +        fr->gbtabscale = 2000;
 +#else
 +        fr->gbtabscale = 500;
 +#endif
 +
 +        fr->gbtabr = 100;
 +        fr->gbtab  = make_gb_table(oenv, fr);
 +
 +        init_gb(&fr->born, fr, ir, mtop, ir->gb_algorithm);
 +
 +        /* Copy local gb data (for dd, this is done in dd_partition_system) */
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            make_local_gb(cr, fr->born, ir->gb_algorithm);
 +        }
 +    }
 +
 +    /* Set the charge scaling */
 +    if (fr->epsilon_r != 0)
 +    {
 +        fr->epsfac = ONE_4PI_EPS0/fr->epsilon_r;
 +    }
 +    else
 +    {
 +        /* eps = 0 is infinite dieletric: no coulomb interactions */
 +        fr->epsfac = 0;
 +    }
 +
 +    /* Reaction field constants */
 +    if (EEL_RF(fr->eeltype))
 +    {
 +        calc_rffac(fp, fr->eeltype, fr->epsilon_r, fr->epsilon_rf,
 +                   fr->rcoulomb, fr->temp, fr->zsquare, box,
 +                   &fr->kappa, &fr->k_rf, &fr->c_rf);
 +    }
 +
 +    /*This now calculates sum for q and c6*/
 +    set_chargesum(fp, fr, mtop);
 +
 +    /* if we are using LR electrostatics, and they are tabulated,
 +     * the tables will contain modified coulomb interactions.
 +     * Since we want to use the non-shifted ones for 1-4
 +     * coulombic interactions, we must have an extra set of tables.
 +     */
 +
 +    /* Construct tables.
 +     * A little unnecessary to make both vdw and coul tables sometimes,
 +     * but what the heck... */
 +
 +    bMakeTables = fr->bcoultab || fr->bvdwtab || fr->bEwald ||
 +        (ir->eDispCorr != edispcNO && ir_vdw_switched(ir));
 +
 +    bMakeSeparate14Table = ((!bMakeTables || fr->eeltype != eelCUT || fr->vdwtype != evdwCUT ||
 +                             fr->bBHAM || fr->bEwald) &&
 +                            (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 ||
 +                             gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0 ||
 +                             gmx_mtop_ftype_count(mtop, F_LJC_PAIRS_NB) > 0));
 +
 +    negp_pp   = ir->opts.ngener - ir->nwall;
 +    negptable = 0;
 +    if (!bMakeTables)
 +    {
 +        bSomeNormalNbListsAreInUse = TRUE;
 +        fr->nnblists               = 1;
 +    }
 +    else
 +    {
 +        bSomeNormalNbListsAreInUse = (ir->eDispCorr != edispcNO);
 +        for (egi = 0; egi < negp_pp; egi++)
 +        {
 +            for (egj = egi; egj < negp_pp; egj++)
 +            {
 +                egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)];
 +                if (!(egp_flags & EGP_EXCL))
 +                {
 +                    if (egp_flags & EGP_TABLE)
 +                    {
 +                        negptable++;
 +                    }
 +                    else
 +                    {
 +                        bSomeNormalNbListsAreInUse = TRUE;
 +                    }
 +                }
 +            }
 +        }
 +        if (bSomeNormalNbListsAreInUse)
 +        {
 +            fr->nnblists = negptable + 1;
 +        }
 +        else
 +        {
 +            fr->nnblists = negptable;
 +        }
 +        if (fr->nnblists > 1)
 +        {
 +            snew(fr->gid2nblists, ir->opts.ngener*ir->opts.ngener);
 +        }
 +    }
 +
 +    if (ir->adress)
 +    {
 +        fr->nnblists *= 2;
 +    }
 +
 +    snew(fr->nblists, fr->nnblists);
 +
 +    /* This code automatically gives table length tabext without cut-off's,
 +     * in that case grompp should already have checked that we do not need
 +     * normal tables and we only generate tables for 1-4 interactions.
 +     */
 +    rtab = ir->rlistlong + ir->tabext;
 +
 +    if (bMakeTables)
 +    {
 +        /* make tables for ordinary interactions */
 +        if (bSomeNormalNbListsAreInUse)
 +        {
 +            make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[0]);
 +            if (ir->adress)
 +            {
 +                make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[fr->nnblists/2]);
 +            }
 +            if (!bMakeSeparate14Table)
 +            {
 +                fr->tab14 = fr->nblists[0].table_elec_vdw;
 +            }
 +            m = 1;
 +        }
 +        else
 +        {
 +            m = 0;
 +        }
 +        if (negptable > 0)
 +        {
 +            /* Read the special tables for certain energy group pairs */
 +            nm_ind = mtop->groups.grps[egcENER].nm_ind;
 +            for (egi = 0; egi < negp_pp; egi++)
 +            {
 +                for (egj = egi; egj < negp_pp; egj++)
 +                {
 +                    egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)];
 +                    if ((egp_flags & EGP_TABLE) && !(egp_flags & EGP_EXCL))
 +                    {
 +                        nbl = &(fr->nblists[m]);
 +                        if (fr->nnblists > 1)
 +                        {
 +                            fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = m;
 +                        }
 +                        /* Read the table file with the two energy groups names appended */
 +                        make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn,
 +                                        *mtop->groups.grpname[nm_ind[egi]],
 +                                        *mtop->groups.grpname[nm_ind[egj]],
 +                                        &fr->nblists[m]);
 +                        if (ir->adress)
 +                        {
 +                            make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn,
 +                                            *mtop->groups.grpname[nm_ind[egi]],
 +                                            *mtop->groups.grpname[nm_ind[egj]],
 +                                            &fr->nblists[fr->nnblists/2+m]);
 +                        }
 +                        m++;
 +                    }
 +                    else if (fr->nnblists > 1)
 +                    {
 +                        fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = 0;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (bMakeSeparate14Table)
 +    {
 +        /* generate extra tables with plain Coulomb for 1-4 interactions only */
 +        fr->tab14 = make_tables(fp, oenv, fr, MASTER(cr), tabpfn, rtab,
 +                                GMX_MAKETABLES_14ONLY);
 +    }
 +
 +    /* Read AdResS Thermo Force table if needed */
 +    if (fr->adress_icor == eAdressICThermoForce)
 +    {
 +        /* old todo replace */
 +
 +        if (ir->adress->n_tf_grps > 0)
 +        {
 +            make_adress_tf_tables(fp, oenv, fr, ir, tabfn, mtop, box);
 +
 +        }
 +        else
 +        {
 +            /* load the default table */
 +            snew(fr->atf_tabs, 1);
 +            fr->atf_tabs[DEFAULT_TF_TABLE] = make_atf_table(fp, oenv, fr, tabafn, box);
 +        }
 +    }
 +
 +    /* Wall stuff */
 +    fr->nwall = ir->nwall;
 +    if (ir->nwall && ir->wall_type == ewtTABLE)
 +    {
 +        make_wall_tables(fp, oenv, ir, tabfn, &mtop->groups, fr);
 +    }
 +
 +    if (fcd && tabbfn)
 +    {
 +        fcd->bondtab  = make_bonded_tables(fp,
 +                                           F_TABBONDS, F_TABBONDSNC,
 +                                           mtop, tabbfn, "b");
 +        fcd->angletab = make_bonded_tables(fp,
 +                                           F_TABANGLES, -1,
 +                                           mtop, tabbfn, "a");
 +        fcd->dihtab   = make_bonded_tables(fp,
 +                                           F_TABDIHS, -1,
 +                                           mtop, tabbfn, "d");
 +    }
 +    else
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "No fcdata or table file name passed, can not read table, can not do bonded interactions\n");
 +        }
 +    }
 +
 +    /* QM/MM initialization if requested
 +     */
 +    if (ir->bQMMM)
 +    {
 +        fprintf(stderr, "QM/MM calculation requested.\n");
 +    }
 +
 +    fr->bQMMM      = ir->bQMMM;
 +    fr->qr         = mk_QMMMrec();
 +
 +    /* Set all the static charge group info */
 +    fr->cginfo_mb = init_cginfo_mb(fp, mtop, fr, bNoSolvOpt,
 +                                   &bFEP_NonBonded,
 +                                   &fr->bExcl_IntraCGAll_InterCGNone);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        fr->cginfo = NULL;
 +    }
 +    else
 +    {
 +        fr->cginfo = cginfo_expand(mtop->nmolblock, fr->cginfo_mb);
 +    }
 +
 +    if (!DOMAINDECOMP(cr))
 +    {
 +        forcerec_set_ranges(fr, ncg_mtop(mtop), ncg_mtop(mtop),
 +                            mtop->natoms, mtop->natoms, mtop->natoms);
 +    }
 +
 +    fr->print_force = print_force;
 +
 +
 +    /* coarse load balancing vars */
 +    fr->t_fnbf    = 0.;
 +    fr->t_wait    = 0.;
 +    fr->timesteps = 0;
 +
 +    /* Initialize neighbor search */
 +    init_ns(fp, cr, &fr->ns, fr, mtop);
 +
 +    if (cr->duty & DUTY_PP)
 +    {
 +        gmx_nonbonded_setup(fr, bGenericKernelOnly);
 +        /*
 +           if (ir->bAdress)
 +            {
 +                gmx_setup_adress_kernels(fp,bGenericKernelOnly);
 +            }
 +         */
 +    }
 +
 +    /* Initialize the thread working data for bonded interactions */
 +    init_forcerec_f_threads(fr, mtop->groups.grps[egcENER].nr);
 +
 +    snew(fr->excl_load, fr->nthreads+1);
 +
 +    if (fr->cutoff_scheme == ecutsVERLET)
 +    {
 +        if (ir->rcoulomb != ir->rvdw)
 +        {
 +            gmx_fatal(FARGS, "With Verlet lists rcoulomb and rvdw should be identical");
 +        }
 +
 +        init_nb_verlet(fp, &fr->nbv, bFEP_NonBonded, ir, fr, cr, nbpu_opt);
 +    }
 +
 +    /* fr->ic is used both by verlet and group kernels (to some extent) now */
 +    init_interaction_const(fp, cr, &fr->ic, fr, rtab);
 +
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        calc_enervirdiff(fp, ir->eDispCorr, fr);
 +    }
 +}
 +
 +#define pr_real(fp, r) fprintf(fp, "%s: %e\n",#r, r)
 +#define pr_int(fp, i)  fprintf((fp), "%s: %d\n",#i, i)
 +#define pr_bool(fp, b) fprintf((fp), "%s: %s\n",#b, bool_names[b])
 +
 +void pr_forcerec(FILE *fp, t_forcerec *fr)
 +{
 +    int i;
 +
 +    pr_real(fp, fr->rlist);
 +    pr_real(fp, fr->rcoulomb);
 +    pr_real(fp, fr->fudgeQQ);
 +    pr_bool(fp, fr->bGrid);
 +    pr_bool(fp, fr->bTwinRange);
 +    /*pr_int(fp,fr->cg0);
 +       pr_int(fp,fr->hcg);*/
 +    for (i = 0; i < fr->nnblists; i++)
 +    {
 +        pr_int(fp, fr->nblists[i].table_elec_vdw.n);
 +    }
 +    pr_real(fp, fr->rcoulomb_switch);
 +    pr_real(fp, fr->rcoulomb);
 +
 +    fflush(fp);
 +}
 +
 +void forcerec_set_excl_load(t_forcerec           *fr,
 +                            const gmx_localtop_t *top)
 +{
 +    const int *ind, *a;
 +    int        t, i, j, ntot, n, ntarget;
 +
 +    ind = top->excls.index;
 +    a   = top->excls.a;
 +
 +    ntot = 0;
 +    for (i = 0; i < top->excls.nr; i++)
 +    {
 +        for (j = ind[i]; j < ind[i+1]; j++)
 +        {
 +            if (a[j] > i)
 +            {
 +                ntot++;
 +            }
 +        }
 +    }
 +
 +    fr->excl_load[0] = 0;
 +    n                = 0;
 +    i                = 0;
 +    for (t = 1; t <= fr->nthreads; t++)
 +    {
 +        ntarget = (ntot*t)/fr->nthreads;
 +        while (i < top->excls.nr && n < ntarget)
 +        {
 +            for (j = ind[i]; j < ind[i+1]; j++)
 +            {
 +                if (a[j] > i)
 +                {
 +                    n++;
 +                }
 +            }
 +            i++;
 +        }
 +        fr->excl_load[t] = i;
 +    }
 +}
index 1558e45fa49755ad9f560a6334fbcb0cc841dff2,0000000000000000000000000000000000000000..748768687bb94b1a5bb9cd5c3ced4ddf6cd88e45
mode 100644,000000..100644
--- /dev/null
@@@ -1,765 -1,0 +1,771 @@@
 +/*
 + * 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 "typedefs.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "mdrun.h"
 +#include "domdec.h"
 +#include "mtop_util.h"
 +#include "vcm.h"
 +#include "nrnb.h"
 +#include "macros.h"
 +#include "md_logging.h"
 +#include "md_support.h"
++#include "names.h"
 +
 +#include "gromacs/timing/wallcycle.h"
 +
 +/* Is the signal in one simulation independent of other simulations? */
 +gmx_bool gs_simlocal[eglsNR] = { TRUE, FALSE, FALSE, TRUE };
 +
 +/* check which of the multisim simulations has the shortest number of
 +   steps and return that number of nsteps */
 +gmx_int64_t get_multisim_nsteps(const t_commrec *cr,
 +                                gmx_int64_t      nsteps)
 +{
 +    gmx_int64_t steps_out;
 +
 +    if (MASTER(cr))
 +    {
 +        gmx_int64_t     *buf;
 +        int              s;
 +
 +        snew(buf, cr->ms->nsim);
 +
 +        buf[cr->ms->sim] = nsteps;
 +        gmx_sumli_sim(cr->ms->nsim, buf, cr->ms);
 +
 +        steps_out = -1;
 +        for (s = 0; s < cr->ms->nsim; s++)
 +        {
 +            /* find the smallest positive number */
 +            if (buf[s] >= 0 && ((steps_out < 0) || (buf[s] < steps_out)) )
 +            {
 +                steps_out = buf[s];
 +            }
 +        }
 +        sfree(buf);
 +
 +        /* if we're the limiting simulation, don't do anything */
 +        if (steps_out >= 0 && steps_out < nsteps)
 +        {
 +            char strbuf[255];
 +            snprintf(strbuf, 255, "Will stop simulation %%d after %s steps (another simulation will end then).\n", "%"GMX_PRId64);
 +            fprintf(stderr, strbuf, cr->ms->sim, steps_out);
 +        }
 +    }
 +    /* broadcast to non-masters */
 +    gmx_bcast(sizeof(gmx_int64_t), &steps_out, cr);
 +    return steps_out;
 +}
 +
 +int multisim_min(const gmx_multisim_t *ms, int nmin, int n)
 +{
 +    int     *buf;
 +    gmx_bool bPos, bEqual;
 +    int      s, d;
 +
 +    snew(buf, ms->nsim);
 +    buf[ms->sim] = n;
 +    gmx_sumi_sim(ms->nsim, buf, ms);
 +    bPos   = TRUE;
 +    bEqual = TRUE;
 +    for (s = 0; s < ms->nsim; s++)
 +    {
 +        bPos   = bPos   && (buf[s] > 0);
 +        bEqual = bEqual && (buf[s] == buf[0]);
 +    }
 +    if (bPos)
 +    {
 +        if (bEqual)
 +        {
 +            nmin = min(nmin, buf[0]);
 +        }
 +        else
 +        {
 +            /* Find the least common multiple */
 +            for (d = 2; d < nmin; d++)
 +            {
 +                s = 0;
 +                while (s < ms->nsim && d % buf[s] == 0)
 +                {
 +                    s++;
 +                }
 +                if (s == ms->nsim)
 +                {
 +                    /* We found the LCM and it is less than nmin */
 +                    nmin = d;
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +    sfree(buf);
 +
 +    return nmin;
 +}
 +
 +int multisim_nstsimsync(const t_commrec *cr,
 +                        const t_inputrec *ir, int repl_ex_nst)
 +{
 +    int nmin;
 +
 +    if (MASTER(cr))
 +    {
 +        nmin = INT_MAX;
 +        nmin = multisim_min(cr->ms, nmin, ir->nstlist);
 +        nmin = multisim_min(cr->ms, nmin, ir->nstcalcenergy);
 +        nmin = multisim_min(cr->ms, nmin, repl_ex_nst);
 +        if (nmin == INT_MAX)
 +        {
 +            gmx_fatal(FARGS, "Can not find an appropriate interval for inter-simulation communication, since nstlist, nstcalcenergy and -replex are all <= 0");
 +        }
 +        /* Avoid inter-simulation communication at every (second) step */
 +        if (nmin <= 2)
 +        {
 +            nmin = 10;
 +        }
 +    }
 +
 +    gmx_bcast(sizeof(int), &nmin, cr);
 +
 +    return nmin;
 +}
 +
 +void init_global_signals(globsig_t *gs, const t_commrec *cr,
 +                         const t_inputrec *ir, int repl_ex_nst)
 +{
 +    int i;
 +
 +    if (MULTISIM(cr))
 +    {
 +        gs->nstms = multisim_nstsimsync(cr, ir, repl_ex_nst);
 +        if (debug)
 +        {
 +            fprintf(debug, "Syncing simulations for checkpointing and termination every %d steps\n", gs->nstms);
 +        }
 +    }
 +    else
 +    {
 +        gs->nstms = 1;
 +    }
 +
 +    for (i = 0; i < eglsNR; i++)
 +    {
 +        gs->sig[i] = 0;
 +        gs->set[i] = 0;
 +    }
 +}
 +
 +void copy_coupling_state(t_state *statea, t_state *stateb,
 +                         gmx_ekindata_t *ekinda, gmx_ekindata_t *ekindb, t_grpopts* opts)
 +{
 +
 +    /* MRS note -- might be able to get rid of some of the arguments.  Look over it when it's all debugged */
 +
 +    int i, j, nc;
 +
 +    /* Make sure we have enough space for x and v */
 +    if (statea->nalloc > stateb->nalloc)
 +    {
 +        stateb->nalloc = statea->nalloc;
 +        srenew(stateb->x, stateb->nalloc);
 +        srenew(stateb->v, stateb->nalloc);
 +    }
 +
 +    stateb->natoms     = statea->natoms;
 +    stateb->ngtc       = statea->ngtc;
 +    stateb->nnhpres    = statea->nnhpres;
 +    stateb->veta       = statea->veta;
 +    if (ekinda)
 +    {
 +        copy_mat(ekinda->ekin, ekindb->ekin);
 +        for (i = 0; i < stateb->ngtc; i++)
 +        {
 +            ekindb->tcstat[i].T  = ekinda->tcstat[i].T;
 +            ekindb->tcstat[i].Th = ekinda->tcstat[i].Th;
 +            copy_mat(ekinda->tcstat[i].ekinh, ekindb->tcstat[i].ekinh);
 +            copy_mat(ekinda->tcstat[i].ekinf, ekindb->tcstat[i].ekinf);
 +            ekindb->tcstat[i].ekinscalef_nhc =  ekinda->tcstat[i].ekinscalef_nhc;
 +            ekindb->tcstat[i].ekinscaleh_nhc =  ekinda->tcstat[i].ekinscaleh_nhc;
 +            ekindb->tcstat[i].vscale_nhc     =  ekinda->tcstat[i].vscale_nhc;
 +        }
 +    }
 +    copy_rvecn(statea->x, stateb->x, 0, stateb->natoms);
 +    copy_rvecn(statea->v, stateb->v, 0, stateb->natoms);
 +    copy_mat(statea->box, stateb->box);
 +    copy_mat(statea->box_rel, stateb->box_rel);
 +    copy_mat(statea->boxv, stateb->boxv);
 +
 +    for (i = 0; i < stateb->ngtc; i++)
 +    {
 +        nc = i*opts->nhchainlength;
 +        for (j = 0; j < opts->nhchainlength; j++)
 +        {
 +            stateb->nosehoover_xi[nc+j]  = statea->nosehoover_xi[nc+j];
 +            stateb->nosehoover_vxi[nc+j] = statea->nosehoover_vxi[nc+j];
 +        }
 +    }
 +    if (stateb->nhpres_xi != NULL)
 +    {
 +        for (i = 0; i < stateb->nnhpres; i++)
 +        {
 +            nc = i*opts->nhchainlength;
 +            for (j = 0; j < opts->nhchainlength; j++)
 +            {
 +                stateb->nhpres_xi[nc+j]  = statea->nhpres_xi[nc+j];
 +                stateb->nhpres_vxi[nc+j] = statea->nhpres_vxi[nc+j];
 +            }
 +        }
 +    }
 +}
 +
 +real compute_conserved_from_auxiliary(t_inputrec *ir, t_state *state, t_extmass *MassQ)
 +{
 +    real quantity = 0;
 +    switch (ir->etc)
 +    {
 +        case etcNO:
 +            break;
 +        case etcBERENDSEN:
 +            break;
 +        case etcNOSEHOOVER:
 +            quantity = NPT_energy(ir, state, MassQ);
 +            break;
 +        case etcVRESCALE:
 +            quantity = vrescale_energy(&(ir->opts), state->therm_integral);
 +            break;
 +        default:
 +            break;
 +    }
 +    return quantity;
 +}
 +
 +void compute_globals(FILE *fplog, gmx_global_stat_t gstat, t_commrec *cr, t_inputrec *ir,
 +                     t_forcerec *fr, gmx_ekindata_t *ekind,
 +                     t_state *state, t_state *state_global, t_mdatoms *mdatoms,
 +                     t_nrnb *nrnb, t_vcm *vcm, gmx_wallcycle_t wcycle,
 +                     gmx_enerdata_t *enerd, tensor force_vir, tensor shake_vir, tensor total_vir,
 +                     tensor pres, rvec mu_tot, gmx_constr_t constr,
 +                     globsig_t *gs, gmx_bool bInterSimGS,
 +                     matrix box, gmx_mtop_t *top_global,
 +                     gmx_bool *bSumEkinhOld, int flags)
 +{
 +    int      i, gsi;
 +    real     gs_buf[eglsNR];
 +    tensor   corr_vir, corr_pres;
 +    gmx_bool bEner, bPres, bTemp, bVV;
 +    gmx_bool bRerunMD, bStopCM, bGStat, bIterate,
 +             bFirstIterate, bReadEkin, bEkinAveVel, bScaleEkin, bConstrain;
 +    real     ekin, temp, prescorr, enercorr, dvdlcorr, dvdl_ekin;
 +
 +    /* translate CGLO flags to gmx_booleans */
 +    bRerunMD = flags & CGLO_RERUNMD;
 +    bStopCM  = flags & CGLO_STOPCM;
 +    bGStat   = flags & CGLO_GSTAT;
 +
 +    bReadEkin     = (flags & CGLO_READEKIN);
 +    bScaleEkin    = (flags & CGLO_SCALEEKIN);
 +    bEner         = flags & CGLO_ENERGY;
 +    bTemp         = flags & CGLO_TEMPERATURE;
 +    bPres         = (flags & CGLO_PRESSURE);
 +    bConstrain    = (flags & CGLO_CONSTRAINT);
 +    bIterate      = (flags & CGLO_ITERATE);
 +    bFirstIterate = (flags & CGLO_FIRSTITERATE);
 +
 +    /* we calculate a full state kinetic energy either with full-step velocity verlet
 +       or half step where we need the pressure */
 +
 +    bEkinAveVel = (ir->eI == eiVV || (ir->eI == eiVVAK && bPres) || bReadEkin);
 +
 +    /* in initalization, it sums the shake virial in vv, and to
 +       sums ekinh_old in leapfrog (or if we are calculating ekinh_old) for other reasons */
 +
 +    /* ########## Kinetic energy  ############## */
 +
 +    if (bTemp)
 +    {
 +        /* Non-equilibrium MD: this is parallellized, but only does communication
 +         * when there really is NEMD.
 +         */
 +
 +        if (PAR(cr) && (ekind->bNEMD))
 +        {
 +            accumulate_u(cr, &(ir->opts), ekind);
 +        }
 +        debug_gmx();
 +        if (bReadEkin)
 +        {
 +            restore_ekinstate_from_state(cr, ekind, &state_global->ekinstate);
 +        }
 +        else
 +        {
 +
 +            calc_ke_part(state, &(ir->opts), mdatoms, ekind, nrnb, bEkinAveVel, bIterate);
 +        }
 +
 +        debug_gmx();
 +    }
 +
 +    /* Calculate center of mass velocity if necessary, also parallellized */
 +    if (bStopCM)
 +    {
 +        calc_vcm_grp(0, mdatoms->homenr, mdatoms,
 +                     state->x, state->v, vcm);
 +    }
 +
 +    if (bTemp || bStopCM || bPres || bEner || bConstrain)
 +    {
 +        if (!bGStat)
 +        {
 +            /* We will not sum ekinh_old,
 +             * so signal that we still have to do it.
 +             */
 +            *bSumEkinhOld = TRUE;
 +
 +        }
 +        else
 +        {
 +            if (gs != NULL)
 +            {
 +                for (i = 0; i < eglsNR; i++)
 +                {
 +                    gs_buf[i] = gs->sig[i];
 +                }
 +            }
 +            if (PAR(cr))
 +            {
 +                wallcycle_start(wcycle, ewcMoveE);
 +                global_stat(fplog, gstat, cr, enerd, force_vir, shake_vir, mu_tot,
 +                            ir, ekind, constr, bStopCM ? vcm : NULL,
 +                            gs != NULL ? eglsNR : 0, gs_buf,
 +                            top_global, state,
 +                            *bSumEkinhOld, flags);
 +                wallcycle_stop(wcycle, ewcMoveE);
 +            }
 +            if (gs != NULL)
 +            {
 +                if (MULTISIM(cr) && bInterSimGS)
 +                {
 +                    if (MASTER(cr))
 +                    {
 +                        /* Communicate the signals between the simulations */
 +                        gmx_sum_sim(eglsNR, gs_buf, cr->ms);
 +                    }
 +                    /* Communicate the signals form the master to the others */
 +                    gmx_bcast(eglsNR*sizeof(gs_buf[0]), gs_buf, cr);
 +                }
 +                for (i = 0; i < eglsNR; i++)
 +                {
 +                    if (bInterSimGS || gs_simlocal[i])
 +                    {
 +                        /* Set the communicated signal only when it is non-zero,
 +                         * since signals might not be processed at each MD step.
 +                         */
 +                        gsi = (gs_buf[i] >= 0 ?
 +                               (int)(gs_buf[i] + 0.5) :
 +                               (int)(gs_buf[i] - 0.5));
 +                        if (gsi != 0)
 +                        {
 +                            gs->set[i] = gsi;
 +                        }
 +                        /* Turn off the local signal */
 +                        gs->sig[i] = 0;
 +                    }
 +                }
 +            }
 +            *bSumEkinhOld = FALSE;
 +        }
 +    }
 +
 +    if (!ekind->bNEMD && debug && bTemp && (vcm->nr > 0))
 +    {
 +        correct_ekin(debug,
 +                     0, mdatoms->homenr,
 +                     state->v, vcm->group_p[0],
 +                     mdatoms->massT, mdatoms->tmass, ekind->ekin);
 +    }
 +
 +    /* Do center of mass motion removal */
 +    if (bStopCM)
 +    {
 +        check_cm_grp(fplog, vcm, ir, 1);
 +        do_stopcm_grp(0, mdatoms->homenr, mdatoms->cVCM,
 +                      state->x, state->v, vcm);
 +        inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr);
 +    }
 +
 +    if (bEner)
 +    {
 +        /* Calculate the amplitude of the cosine velocity profile */
 +        ekind->cosacc.vcos = ekind->cosacc.mvcos/mdatoms->tmass;
 +    }
 +
 +    if (bTemp)
 +    {
 +        /* Sum the kinetic energies of the groups & calc temp */
 +        /* compute full step kinetic energies if vv, or if vv-avek and we are computing the pressure with IR_NPT_TROTTER */
 +        /* three maincase:  VV with AveVel (md-vv), vv with AveEkin (md-vv-avek), leap with AveEkin (md).
 +           Leap with AveVel is not supported; it's not clear that it will actually work.
 +           bEkinAveVel: If TRUE, we simply multiply ekin by ekinscale to get a full step kinetic energy.
 +           If FALSE, we average ekinh_old and ekinh*ekinscale_nhc to get an averaged half step kinetic energy.
 +           bSaveEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't reset the ekinscale_nhc.
 +           If FALSE, we go ahead and erase over it.
 +         */
 +        enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, &dvdl_ekin,
 +                                       bEkinAveVel, bScaleEkin);
 +        enerd->dvdl_lin[efptMASS] = (double) dvdl_ekin;
 +
 +        enerd->term[F_EKIN] = trace(ekind->ekin);
 +    }
 +
 +    /* ##########  Long range energy information ###### */
 +
 +    if (bEner || bPres || bConstrain)
 +    {
 +        calc_dispcorr(fplog, ir, fr, 0, top_global->natoms, box, state->lambda[efptVDW],
 +                      corr_pres, corr_vir, &prescorr, &enercorr, &dvdlcorr);
 +    }
 +
 +    if (bEner && bFirstIterate)
 +    {
 +        enerd->term[F_DISPCORR]  = enercorr;
 +        enerd->term[F_EPOT]     += enercorr;
 +        enerd->term[F_DVDL_VDW] += dvdlcorr;
 +    }
 +
 +    /* ########## Now pressure ############## */
 +    if (bPres || bConstrain)
 +    {
 +
 +        m_add(force_vir, shake_vir, total_vir);
 +
 +        /* Calculate pressure and apply LR correction if PPPM is used.
 +         * Use the box from last timestep since we already called update().
 +         */
 +
 +        enerd->term[F_PRES] = calc_pres(fr->ePBC, ir->nwall, box, ekind->ekin, total_vir, pres);
 +
 +        /* Calculate long range corrections to pressure and energy */
 +        /* this adds to enerd->term[F_PRES] and enerd->term[F_ETOT],
 +           and computes enerd->term[F_DISPCORR].  Also modifies the
 +           total_vir and pres tesors */
 +
 +        m_add(total_vir, corr_vir, total_vir);
 +        m_add(pres, corr_pres, pres);
 +        enerd->term[F_PDISPCORR] = prescorr;
 +        enerd->term[F_PRES]     += prescorr;
 +    }
 +}
 +
 +void check_nst_param(FILE *fplog, t_commrec *cr,
 +                     const char *desc_nst, int nst,
 +                     const char *desc_p, int *p)
 +{
 +    if (*p > 0 && *p % nst != 0)
 +    {
 +        /* Round up to the next multiple of nst */
 +        *p = ((*p)/nst + 1)*nst;
 +        md_print_warn(cr, fplog,
 +                      "NOTE: %s changes %s to %d\n", desc_nst, desc_p, *p);
 +    }
 +}
 +
 +void set_current_lambdas(gmx_int64_t step, t_lambda *fepvals, gmx_bool bRerunMD,
 +                         t_trxframe *rerun_fr, t_state *state_global, t_state *state, double lam0[])
 +/* find the current lambdas.  If rerunning, we either read in a state, or a lambda value,
 +   requiring different logic. */
 +{
 +    real frac;
 +    int  i, fep_state = 0;
 +    if (bRerunMD)
 +    {
 +        if (rerun_fr->bLambda)
 +        {
 +            if (fepvals->delta_lambda == 0)
 +            {
 +                state_global->lambda[efptFEP] = rerun_fr->lambda;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    if (i != efptFEP)
 +                    {
 +                        state->lambda[i] = state_global->lambda[i];
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                /* find out between which two value of lambda we should be */
 +                frac      = (step*fepvals->delta_lambda);
 +                fep_state = floor(frac*fepvals->n_lambda);
 +                /* interpolate between this state and the next */
 +                /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
 +                frac = (frac*fepvals->n_lambda)-fep_state;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) +
 +                        frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]);
 +                }
 +            }
 +        }
 +        else if (rerun_fr->bFepState)
 +        {
 +            state_global->fep_state = rerun_fr->fep_state;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                state_global->lambda[i] = fepvals->all_lambda[i][fep_state];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        if (fepvals->delta_lambda != 0)
 +        {
 +            /* find out between which two value of lambda we should be */
 +            frac = (step*fepvals->delta_lambda);
 +            if (fepvals->n_lambda > 0)
 +            {
 +                fep_state = floor(frac*fepvals->n_lambda);
 +                /* interpolate between this state and the next */
 +                /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
 +                frac = (frac*fepvals->n_lambda)-fep_state;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) +
 +                        frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + frac;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            if (state->fep_state > 0)
 +            {
 +                state_global->fep_state = state->fep_state; /* state->fep is the one updated by bExpanded */
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = fepvals->all_lambda[i][state_global->fep_state];
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        state->lambda[i] = state_global->lambda[i];
 +    }
 +}
 +
 +static void min_zero(int *n, int i)
 +{
 +    if (i > 0 && (*n == 0 || i < *n))
 +    {
 +        *n = i;
 +    }
 +}
 +
 +static int lcd4(int i1, int i2, int i3, int i4)
 +{
 +    int nst;
 +
 +    nst = 0;
 +    min_zero(&nst, i1);
 +    min_zero(&nst, i2);
 +    min_zero(&nst, i3);
 +    min_zero(&nst, i4);
 +    if (nst == 0)
 +    {
 +        gmx_incons("All 4 inputs for determininig nstglobalcomm are <= 0");
 +    }
 +
 +    while (nst > 1 && ((i1 > 0 && i1 % nst != 0)  ||
 +                       (i2 > 0 && i2 % nst != 0)  ||
 +                       (i3 > 0 && i3 % nst != 0)  ||
 +                       (i4 > 0 && i4 % nst != 0)))
 +    {
 +        nst--;
 +    }
 +
 +    return nst;
 +}
 +
 +int check_nstglobalcomm(FILE *fplog, t_commrec *cr,
 +                        int nstglobalcomm, t_inputrec *ir)
 +{
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        nstglobalcomm = 1;
 +    }
 +
 +    if (nstglobalcomm == -1)
 +    {
 +        if (!(ir->nstcalcenergy > 0 ||
 +              ir->nstlist > 0 ||
 +              ir->etc != etcNO ||
 +              ir->epc != epcNO))
 +        {
 +            nstglobalcomm = 10;
 +            if (ir->nstenergy > 0 && ir->nstenergy < nstglobalcomm)
 +            {
 +                nstglobalcomm = ir->nstenergy;
 +            }
 +        }
 +        else
 +        {
 +            /* Ensure that we do timely global communication for
 +             * (possibly) each of the four following options.
 +             */
 +            nstglobalcomm = lcd4(ir->nstcalcenergy,
 +                                 ir->nstlist,
 +                                 ir->etc != etcNO ? ir->nsttcouple : 0,
 +                                 ir->epc != epcNO ? ir->nstpcouple : 0);
 +        }
 +    }
 +    else
 +    {
 +        if (ir->nstlist > 0 &&
 +            nstglobalcomm > ir->nstlist && nstglobalcomm % ir->nstlist != 0)
 +        {
 +            nstglobalcomm = (nstglobalcomm / ir->nstlist)*ir->nstlist;
 +            md_print_warn(cr, fplog, "WARNING: nstglobalcomm is larger than nstlist, but not a multiple, setting it to %d\n", nstglobalcomm);
 +        }
 +        if (ir->nstcalcenergy > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nstcalcenergy", &ir->nstcalcenergy);
 +        }
 +        if (ir->etc != etcNO && ir->nsttcouple > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nsttcouple", &ir->nsttcouple);
 +        }
 +        if (ir->epc != epcNO && ir->nstpcouple > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nstpcouple", &ir->nstpcouple);
 +        }
 +
 +        check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                        "nstenergy", &ir->nstenergy);
 +
 +        check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                        "nstlog", &ir->nstlog);
 +    }
 +
 +    if (ir->comm_mode != ecmNO && ir->nstcomm < nstglobalcomm)
 +    {
 +        md_print_warn(cr, fplog, "WARNING: Changing nstcomm from %d to %d\n",
 +                      ir->nstcomm, nstglobalcomm);
 +        ir->nstcomm = nstglobalcomm;
 +    }
 +
 +    return nstglobalcomm;
 +}
 +
 +void check_ir_old_tpx_versions(t_commrec *cr, FILE *fplog,
 +                               t_inputrec *ir, gmx_mtop_t *mtop)
 +{
 +    /* Check required for old tpx files */
 +    if (IR_TWINRANGE(*ir) && ir->nstlist > 1 &&
 +        ir->nstcalcenergy % ir->nstlist != 0)
 +    {
 +        md_print_warn(cr, fplog, "Old tpr file with twin-range settings: modifying energy calculation and/or T/P-coupling frequencies\n");
 +
 +        if (gmx_mtop_ftype_count(mtop, F_CONSTR) +
 +            gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0 &&
 +            ir->eConstrAlg == econtSHAKE)
 +        {
 +            md_print_warn(cr, fplog, "With twin-range cut-off's and SHAKE the virial and pressure are incorrect\n");
 +            if (ir->epc != epcNO)
 +            {
 +                gmx_fatal(FARGS, "Can not do pressure coupling with twin-range cut-off's and SHAKE");
 +            }
 +        }
 +        check_nst_param(fplog, cr, "nstlist", ir->nstlist,
 +                        "nstcalcenergy", &ir->nstcalcenergy);
 +        if (ir->epc != epcNO)
 +        {
 +            check_nst_param(fplog, cr, "nstlist", ir->nstlist,
 +                            "nstpcouple", &ir->nstpcouple);
 +        }
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "nstenergy", &ir->nstenergy);
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "nstlog", &ir->nstlog);
 +        if (ir->efep != efepNO)
 +        {
 +            check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                            "nstdhdl", &ir->fepvals->nstdhdl);
 +        }
 +    }
++
++    if (EI_VV(ir->eI) && IR_TWINRANGE(*ir) && ir->nstlist > 1)
++    {
++        gmx_fatal(FARGS, "Twin-range multiple time stepping does not work with integrator %s.", ei_names[ir->eI]);
++    }
 +}
 +
 +void rerun_parallel_comm(t_commrec *cr, t_trxframe *fr,
 +                         gmx_bool *bNotLastFrame)
 +{
 +    gmx_bool bAlloc;
 +    rvec    *xp, *vp;
 +
 +    bAlloc = (fr->natoms == 0);
 +
 +    if (MASTER(cr) && !*bNotLastFrame)
 +    {
 +        fr->natoms = -1;
 +    }
 +    xp = fr->x;
 +    vp = fr->v;
 +    gmx_bcast(sizeof(*fr), fr, cr);
 +    fr->x = xp;
 +    fr->v = vp;
 +
 +    *bNotLastFrame = (fr->natoms >= 0);
 +
 +}
index b2200918f8fc8212fee8b46725a698c495e10d00,0000000000000000000000000000000000000000..a4500a4d4e24a749eb2ae0e9a0509245787190b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,1272 -1,0 +1,1272 @@@
-         gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f)",
 +/*
 + * 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 <math.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "vec.h"
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "network.h"
 +#include "gromacs/fileio/filenm.h"
 +#include <string.h>
 +#include "gromacs/utility/smalloc.h"
 +#include "pull.h"
 +#include "xvgr.h"
 +#include "names.h"
 +#include "pbc.h"
 +#include "mtop_util.h"
 +#include "mdrun.h"
 +#include "gmx_ga2la.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +#include "vec.h"
 +
 +static void pull_print_group_x(FILE *out, ivec dim, const t_pull_group *pgrp)
 +{
 +    int m;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        if (dim[m])
 +        {
 +            fprintf(out, "\t%g", pgrp->x[m]);
 +        }
 +    }
 +}
 +
 +static void pull_print_coord_dr(FILE *out, ivec dim, const t_pull_coord *pcrd)
 +{
 +    int m;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        if (dim[m])
 +        {
 +            fprintf(out, "\t%g", pcrd->dr[m]);
 +        }
 +    }
 +}
 +
 +static void pull_print_x(FILE *out, t_pull *pull, double t)
 +{
 +    int                 c;
 +    const t_pull_coord *pcrd;
 +
 +    fprintf(out, "%.4f", t);
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (pull->bPrintRef)
 +        {
 +            if (PULL_CYL(pull))
 +            {
 +                pull_print_group_x(out, pull->dim, &pull->dyna[c]);
 +            }
 +            else
 +            {
 +                pull_print_group_x(out, pull->dim, &pull->group[pcrd->group[0]]);
 +            }
 +        }
 +        pull_print_coord_dr(out, pull->dim, pcrd);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +static void pull_print_f(FILE *out, t_pull *pull, double t)
 +{
 +    int c, d;
 +
 +    fprintf(out, "%.4f", t);
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        fprintf(out, "\t%g", pull->coord[c].f_scal);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +void pull_print_output(t_pull *pull, gmx_int64_t step, double time)
 +{
 +    if ((pull->nstxout != 0) && (step % pull->nstxout == 0))
 +    {
 +        pull_print_x(pull->out_x, pull, time);
 +    }
 +
 +    if ((pull->nstfout != 0) && (step % pull->nstfout == 0))
 +    {
 +        pull_print_f(pull->out_f, pull, time);
 +    }
 +}
 +
 +static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv,
 +                           gmx_bool bCoord, unsigned long Flags)
 +{
 +    FILE  *fp;
 +    int    nsets, c, m;
 +    char **setname, buf[10];
 +
 +    if (Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a+");
 +    }
 +    else
 +    {
 +        fp = gmx_fio_fopen(fn, "w+");
 +        if (bCoord)
 +        {
 +            xvgr_header(fp, "Pull COM",  "Time (ps)", "Position (nm)",
 +                        exvggtXNY, oenv);
 +        }
 +        else
 +        {
 +            xvgr_header(fp, "Pull force", "Time (ps)", "Force (kJ/mol/nm)",
 +                        exvggtXNY, oenv);
 +        }
 +
 +        snew(setname, 2*pull->ncoord*DIM);
 +        nsets = 0;
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (bCoord)
 +            {
 +                if (pull->bPrintRef)
 +                {
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        if (pull->dim[m])
 +                        {
 +                            sprintf(buf, "%d %s%c", c+1, "c", 'X'+m);
 +                            setname[nsets] = strdup(buf);
 +                            nsets++;
 +                        }
 +                    }
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (pull->dim[m])
 +                    {
 +                        sprintf(buf, "%d %s%c", c+1, "d", 'X'+m);
 +                        setname[nsets] = strdup(buf);
 +                        nsets++;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                sprintf(buf, "%d", c+1);
 +                setname[nsets] = strdup(buf);
 +                nsets++;
 +            }
 +        }
 +        if (nsets > 1)
 +        {
 +            xvgr_legend(fp, nsets, (const char**)setname, oenv);
 +        }
 +        for (c = 0; c < nsets; c++)
 +        {
 +            sfree(setname[c]);
 +        }
 +        sfree(setname);
 +    }
 +
 +    return fp;
 +}
 +
 +/* Apply forces in a mass weighted fashion */
 +static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md,
 +                             const dvec f_pull, int sign, rvec *f)
 +{
 +    int    i, ii, m;
 +    double wmass, inv_wm;
 +
 +    inv_wm = pgrp->wscale*pgrp->invtm;
 +
 +    for (i = 0; i < pgrp->nat_loc; i++)
 +    {
 +        ii    = pgrp->ind_loc[i];
 +        wmass = md->massT[ii];
 +        if (pgrp->weight_loc)
 +        {
 +            wmass *= pgrp->weight_loc[i];
 +        }
 +
 +        for (m = 0; m < DIM; m++)
 +        {
 +            f[ii][m] += sign * wmass * f_pull[m] * inv_wm;
 +        }
 +    }
 +}
 +
 +/* Apply forces in a mass weighted fashion */
 +static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f)
 +{
 +    int                 c;
 +    const t_pull_coord *pcrd;
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (PULL_CYL(pull))
 +        {
 +            apply_forces_grp(&pull->dyna[c], md, pcrd->f, -1, f);
 +        }
 +        else
 +        {
 +            if (pull->group[pcrd->group[0]].nat > 0)
 +            {
 +                apply_forces_grp(&pull->group[pcrd->group[0]], md, pcrd->f, -1, f);
 +            }
 +        }
 +        apply_forces_grp(&pull->group[pcrd->group[1]], md, pcrd->f, 1, f);
 +    }
 +}
 +
 +static double max_pull_distance2(const t_pull *pull, const t_pbc *pbc)
 +{
 +    double max_d2;
 +    int    m;
 +
 +    max_d2 = GMX_DOUBLE_MAX;
 +
 +    if (pull->eGeom != epullgDIRPBC)
 +    {
 +        for (m = 0; m < pbc->ndim_ePBC; m++)
 +        {
 +            if (pull->dim[m] != 0)
 +            {
 +                max_d2 = min(max_d2, norm2(pbc->box[m]));
 +            }
 +        }
 +    }
 +
 +    return 0.25*max_d2;
 +}
 +
 +static void low_get_pull_coord_dr(const t_pull *pull,
 +                                  const t_pull_coord *pcrd,
 +                                  const t_pbc *pbc, double t,
 +                                  dvec xg, dvec xref, double max_dist2,
 +                                  dvec dr)
 +{
 +    const t_pull_group *pgrp0, *pgrp1;
 +    int                 m;
 +    dvec                xrefr, dref = {0, 0, 0};
 +    double              dr2;
 +
 +    pgrp0 = &pull->group[pcrd->group[0]];
 +    pgrp1 = &pull->group[pcrd->group[1]];
 +
 +    /* Only the first group can be an absolute reference, in that case nat=0 */
 +    if (pgrp0->nat == 0)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            xref[m] = pcrd->origin[m];
 +        }
 +    }
 +
 +    copy_dvec(xref, xrefr);
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dref[m] = (pcrd->init + pcrd->rate*t)*pcrd->vec[m];
 +        }
 +        /* Add the reference position, so we use the correct periodic image */
 +        dvec_inc(xrefr, dref);
 +    }
 +
 +    pbc_dx_d(pbc, xg, xrefr, dr);
 +    dr2 = 0;
 +    for (m = 0; m < DIM; m++)
 +    {
 +        dr[m] *= pull->dim[m];
 +        dr2   += dr[m]*dr[m];
 +    }
 +    if (max_dist2 >= 0 && dr2 > 0.98*0.98*max_dist2)
 +    {
++        gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\nYou might want to consider using \"pull-geometry = direction-periodic\" instead.\n",
 +                  pcrd->group[0], pcrd->group[1], sqrt(dr2), sqrt(max_dist2));
 +    }
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        dvec_inc(dr, dref);
 +    }
 +}
 +
 +static void get_pull_coord_dr(const t_pull *pull,
 +                              int coord_ind,
 +                              const t_pbc *pbc, double t,
 +                              dvec dr)
 +{
 +    double              md2;
 +    const t_pull_coord *pcrd;
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        md2 = -1;
 +    }
 +    else
 +    {
 +        md2 = max_pull_distance2(pull, pbc);
 +    }
 +
 +    pcrd = &pull->coord[coord_ind];
 +
 +    low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                          pull->group[pcrd->group[1]].x,
 +                          PULL_CYL(pull) ? pull->dyna[coord_ind].x : pull->group[pcrd->group[0]].x,
 +                          md2,
 +                          dr);
 +}
 +
 +void get_pull_coord_distance(const t_pull *pull,
 +                             int coord_ind,
 +                             const t_pbc *pbc, double t,
 +                             dvec dr, double *dev)
 +{
 +    static gmx_bool     bWarned = FALSE; /* TODO: this should be fixed for thread-safety,
 +                                            but is fairly benign */
 +    const t_pull_coord *pcrd;
 +    int                 m;
 +    double              ref, drs, inpr;
 +
 +    pcrd = &pull->coord[coord_ind];
 +
 +    get_pull_coord_dr(pull, coord_ind, pbc, t, dr);
 +
 +    ref = pcrd->init + pcrd->rate*t;
 +
 +    switch (pull->eGeom)
 +    {
 +        case epullgDIST:
 +            /* Pull along the vector between the com's */
 +            if (ref < 0 && !bWarned)
 +            {
 +                fprintf(stderr, "\nPull reference distance for coordinate %d is negative (%f)\n", coord_ind+1, ref);
 +                bWarned = TRUE;
 +            }
 +            drs = dnorm(dr);
 +            if (drs == 0)
 +            {
 +                /* With no vector we can not determine the direction for the force,
 +                 * so we set the force to zero.
 +                 */
 +                *dev = 0;
 +            }
 +            else
 +            {
 +                /* Determine the deviation */
 +                *dev = drs - ref;
 +            }
 +            break;
 +        case epullgDIR:
 +        case epullgDIRPBC:
 +        case epullgCYL:
 +            /* Pull along vec */
 +            inpr = 0;
 +            for (m = 0; m < DIM; m++)
 +            {
 +                inpr += pcrd->vec[m]*dr[m];
 +            }
 +            *dev = inpr - ref;
 +            break;
 +    }
 +}
 +
 +void clear_pull_forces(t_pull *pull)
 +{
 +    int i;
 +
 +    /* Zeroing the forces is only required for constraint pulling.
 +     * It can happen that multiple constraint steps need to be applied
 +     * and therefore the constraint forces need to be accumulated.
 +     */
 +    for (i = 0; i < pull->ncoord; i++)
 +    {
 +        clear_dvec(pull->coord[i].f);
 +        pull->coord[i].f_scal = 0;
 +    }
 +}
 +
 +/* Apply constraint using SHAKE */
 +static void do_constraint(t_pull *pull, t_pbc *pbc,
 +                          rvec *x, rvec *v,
 +                          gmx_bool bMaster, tensor vir,
 +                          double dt, double t)
 +{
 +
 +    dvec         *r_ij;   /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
 +    dvec          unc_ij; /* xp[i] com of i this step, before constr.   -> unc_ij  */
 +    dvec         *rnew;   /* current 'new' positions of the groups */
 +    double       *dr_tot; /* the total update of the coords */
 +    double        ref;
 +    dvec          vec;
 +    double        d0, inpr;
 +    double        lambda, rm, mass, invdt = 0;
 +    gmx_bool      bConverged_all, bConverged = FALSE;
 +    int           niter = 0, g, c, ii, j, m, max_iter = 100;
 +    double        a;
 +    dvec          f;       /* the pull force */
 +    dvec          tmp, tmp3;
 +    t_pull_group *pdyna, *pgrp0, *pgrp1;
 +    t_pull_coord *pcrd;
 +
 +    snew(r_ij,   pull->ncoord);
 +    snew(dr_tot, pull->ncoord);
 +
 +    snew(rnew, pull->ngroup);
 +
 +    /* copy the current unconstrained positions for use in iterations. We
 +       iterate until rinew[i] and rjnew[j] obey the constraints. Then
 +       rinew - pull.x_unc[i] is the correction dr to group i */
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        copy_dvec(pull->group[g].xp, rnew[g]);
 +    }
 +    if (PULL_CYL(pull))
 +    {
 +        /* There is only one pull coordinate and reference group */
 +        copy_dvec(pull->dyna[0].xp, rnew[pull->coord[0].group[0]]);
 +    }
 +
 +    /* Determine the constraint directions from the old positions */
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        get_pull_coord_dr(pull, c, pbc, t, r_ij[c]);
 +        /* Store the difference vector at time t for printing */
 +        copy_dvec(r_ij[c], pull->coord[c].dr);
 +        if (debug)
 +        {
 +            fprintf(debug, "Pull coord %d dr %f %f %f\n",
 +                    c, r_ij[c][XX], r_ij[c][YY], r_ij[c][ZZ]);
 +        }
 +
 +        if (pull->eGeom == epullgDIR || pull->eGeom == epullgDIRPBC)
 +        {
 +            /* Select the component along vec */
 +            a = 0;
 +            for (m = 0; m < DIM; m++)
 +            {
 +                a += pull->coord[c].vec[m]*r_ij[c][m];
 +            }
 +            for (m = 0; m < DIM; m++)
 +            {
 +                r_ij[c][m] = a*pull->coord[c].vec[m];
 +            }
 +        }
 +    }
 +
 +    bConverged_all = FALSE;
 +    while (!bConverged_all && niter < max_iter)
 +    {
 +        bConverged_all = TRUE;
 +
 +        /* loop over all constraints */
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            dvec dr0, dr1;
 +
 +            pcrd  = &pull->coord[c];
 +            pgrp0 = &pull->group[pcrd->group[0]];
 +            pgrp1 = &pull->group[pcrd->group[1]];
 +
 +            /* Get the current difference vector */
 +            low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                                  rnew[pcrd->group[1]],
 +                                  rnew[pcrd->group[0]],
 +                                  -1, unc_ij);
 +
 +            ref = pcrd->init + pcrd->rate*t;
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "Pull coord %d, iteration %d\n", c, niter);
 +            }
 +
 +            rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
 +
 +            switch (pull->eGeom)
 +            {
 +                case epullgDIST:
 +                    if (ref <= 0)
 +                    {
 +                        gmx_fatal(FARGS, "The pull constraint reference distance for group %d is <= 0 (%f)", c, ref);
 +                    }
 +
 +                    {
 +                        double q, c_a, c_b, c_c;
 +
 +                        c_a = diprod(r_ij[c], r_ij[c]);
 +                        c_b = diprod(unc_ij, r_ij[c])*2;
 +                        c_c = diprod(unc_ij, unc_ij) - dsqr(ref);
 +
 +                        if (c_b < 0)
 +                        {
 +                            q      = -0.5*(c_b - sqrt(c_b*c_b - 4*c_a*c_c));
 +                            lambda = -q/c_a;
 +                        }
 +                        else
 +                        {
 +                            q      = -0.5*(c_b + sqrt(c_b*c_b - 4*c_a*c_c));
 +                            lambda = -c_c/q;
 +                        }
 +
 +                        if (debug)
 +                        {
 +                            fprintf(debug,
 +                                    "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
 +                                    c_a, c_b, c_c, lambda);
 +                        }
 +                    }
 +
 +                    /* The position corrections dr due to the constraints */
 +                    dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
 +                    dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
 +                    dr_tot[c] += -lambda*dnorm(r_ij[c]);
 +                    break;
 +                case epullgDIR:
 +                case epullgDIRPBC:
 +                case epullgCYL:
 +                    /* A 1-dimensional constraint along a vector */
 +                    a = 0;
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vec[m] = pcrd->vec[m];
 +                        a     += unc_ij[m]*vec[m];
 +                    }
 +                    /* Select only the component along the vector */
 +                    dsvmul(a, vec, unc_ij);
 +                    lambda = a - ref;
 +                    if (debug)
 +                    {
 +                        fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
 +                    }
 +
 +                    /* The position corrections dr due to the constraints */
 +                    dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
 +                    dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
 +                    dr_tot[c] += -lambda;
 +                    break;
 +            }
 +
 +            /* DEBUG */
 +            if (debug)
 +            {
 +                int g0, g1;
 +
 +                g0 = pcrd->group[0];
 +                g1 = pcrd->group[1];
 +                low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[g1], rnew[g0], -1, tmp);
 +                low_get_pull_coord_dr(pull, pcrd, pbc, t, dr1, dr0, -1, tmp3);
 +                fprintf(debug,
 +                        "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 +                        rnew[g0][0], rnew[g0][1], rnew[g0][2],
 +                        rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
 +                fprintf(debug,
 +                        "Pull ref %8s %8s %8s   %8s %8s %8s d: %8.5f\n",
 +                        "", "", "", "", "", "", ref);
 +                fprintf(debug,
 +                        "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 +                        dr0[0], dr0[1], dr0[2],
 +                        dr1[0], dr1[1], dr1[2],
 +                        dnorm(tmp3));
 +            } /* END DEBUG */
 +
 +            /* Update the COMs with dr */
 +            dvec_inc(rnew[pcrd->group[1]], dr1);
 +            dvec_inc(rnew[pcrd->group[0]], dr0);
 +        }
 +
 +        /* Check if all constraints are fullfilled now */
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            pcrd = &pull->coord[c];
 +
 +            low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                                  rnew[pcrd->group[1]],
 +                                  rnew[pcrd->group[0]],
 +                                  -1, unc_ij);
 +
 +            switch (pull->eGeom)
 +            {
 +                case epullgDIST:
 +                    bConverged = fabs(dnorm(unc_ij) - ref) < pull->constr_tol;
 +                    break;
 +                case epullgDIR:
 +                case epullgDIRPBC:
 +                case epullgCYL:
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vec[m] = pcrd->vec[m];
 +                    }
 +                    inpr = diprod(unc_ij, vec);
 +                    dsvmul(inpr, vec, unc_ij);
 +                    bConverged =
 +                        fabs(diprod(unc_ij, vec) - ref) < pull->constr_tol;
 +                    break;
 +            }
 +
 +            if (!bConverged)
 +            {
 +                if (debug)
 +                {
 +                    fprintf(debug, "NOT CONVERGED YET: Group %d:"
 +                            "d_ref = %f, current d = %f\n",
 +                            g, ref, dnorm(unc_ij));
 +                }
 +
 +                bConverged_all = FALSE;
 +            }
 +        }
 +
 +        niter++;
 +        /* if after all constraints are dealt with and bConverged is still TRUE
 +           we're finished, if not we do another iteration */
 +    }
 +    if (niter > max_iter)
 +    {
 +        gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
 +    }
 +
 +    /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
 +
 +    if (v)
 +    {
 +        invdt = 1/dt;
 +    }
 +
 +    /* update atoms in the groups */
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        const t_pull_group *pgrp;
 +        dvec                dr;
 +
 +        if (PULL_CYL(pull) && g == pull->coord[0].group[0])
 +        {
 +            pgrp = &pull->dyna[0];
 +        }
 +        else
 +        {
 +            pgrp = &pull->group[g];
 +        }
 +
 +        /* get the final constraint displacement dr for group g */
 +        dvec_sub(rnew[g], pgrp->xp, dr);
 +        /* select components of dr */
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dr[m] *= pull->dim[m];
 +        }
 +
 +        /* update the atom positions */
 +        copy_dvec(dr, tmp);
 +        for (j = 0; j < pgrp->nat_loc; j++)
 +        {
 +            ii = pgrp->ind_loc[j];
 +            if (pgrp->weight_loc)
 +            {
 +                dsvmul(pgrp->wscale*pgrp->weight_loc[j], dr, tmp);
 +            }
 +            for (m = 0; m < DIM; m++)
 +            {
 +                x[ii][m] += tmp[m];
 +            }
 +            if (v)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    v[ii][m] += invdt*tmp[m];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* calculate the constraint forces, used for output and virial only */
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd         = &pull->coord[c];
 +        pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->group[0]].invtm + pull->group[pcrd->group[1]].invtm)*dt*dt);
 +
 +        if (vir && bMaster)
 +        {
 +            double f_invr;
 +
 +            /* Add the pull contribution to the virial */
 +            f_invr = pcrd->f_scal/dnorm(r_ij[c]);
 +
 +            for (j = 0; j < DIM; j++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* finished! I hope. Give back some memory */
 +    sfree(r_ij);
 +    sfree(dr_tot);
 +    sfree(rnew);
 +}
 +
 +/* Pulling with a harmonic umbrella potential or constant force */
 +static void do_pull_pot(int ePull,
 +                        t_pull *pull, t_pbc *pbc, double t, real lambda,
 +                        real *V, tensor vir, real *dVdl)
 +{
 +    int           c, j, m;
 +    double        dev, ndr, invdr;
 +    real          k, dkdl;
 +    t_pull_coord *pcrd;
 +
 +    /* loop over the pull coordinates */
 +    *V    = 0;
 +    *dVdl = 0;
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        get_pull_coord_distance(pull, c, pbc, t, pcrd->dr, &dev);
 +
 +        k    = (1.0 - lambda)*pcrd->k + lambda*pcrd->kB;
 +        dkdl = pcrd->kB - pcrd->k;
 +
 +        switch (pull->eGeom)
 +        {
 +            case epullgDIST:
 +                ndr   = dnorm(pcrd->dr);
 +                invdr = 1/ndr;
 +                if (ePull == epullUMBRELLA)
 +                {
 +                    pcrd->f_scal  =       -k*dev;
 +                    *V           += 0.5*   k*dsqr(dev);
 +                    *dVdl        += 0.5*dkdl*dsqr(dev);
 +                }
 +                else
 +                {
 +                    pcrd->f_scal  =   -k;
 +                    *V           +=    k*ndr;
 +                    *dVdl        += dkdl*ndr;
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    pcrd->f[m]    = pcrd->f_scal*pcrd->dr[m]*invdr;
 +                }
 +                break;
 +            case epullgDIR:
 +            case epullgDIRPBC:
 +            case epullgCYL:
 +                if (ePull == epullUMBRELLA)
 +                {
 +                    pcrd->f_scal  =       -k*dev;
 +                    *V           += 0.5*   k*dsqr(dev);
 +                    *dVdl        += 0.5*dkdl*dsqr(dev);
 +                }
 +                else
 +                {
 +                    ndr = 0;
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        ndr += pcrd->vec[m]*pcrd->dr[m];
 +                    }
 +                    pcrd->f_scal  =   -k;
 +                    *V           +=    k*ndr;
 +                    *dVdl        += dkdl*ndr;
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    pcrd->f[m]    = pcrd->f_scal*pcrd->vec[m];
 +                }
 +                break;
 +        }
 +
 +        if (vir)
 +        {
 +            /* Add the pull contribution to the virial */
 +            for (j = 0; j < DIM; j++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    vir[j][m] -= 0.5*pcrd->f[j]*pcrd->dr[m];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +real pull_potential(int ePull, t_pull *pull, t_mdatoms *md, t_pbc *pbc,
 +                    t_commrec *cr, double t, real lambda,
 +                    rvec *x, rvec *f, tensor vir, real *dvdlambda)
 +{
 +    real V, dVdl;
 +
 +    pull_calc_coms(cr, pull, md, pbc, t, x, NULL);
 +
 +    do_pull_pot(ePull, pull, pbc, t, lambda,
 +                &V, pull->bVirial && MASTER(cr) ? vir : NULL, &dVdl);
 +
 +    /* Distribute forces over pulled groups */
 +    apply_forces(pull, md, f);
 +
 +    if (MASTER(cr))
 +    {
 +        *dvdlambda += dVdl;
 +    }
 +
 +    return (MASTER(cr) ? V : 0.0);
 +}
 +
 +void pull_constraint(t_pull *pull, t_mdatoms *md, t_pbc *pbc,
 +                     t_commrec *cr, double dt, double t,
 +                     rvec *x, rvec *xp, rvec *v, tensor vir)
 +{
 +    pull_calc_coms(cr, pull, md, pbc, t, x, xp);
 +
 +    do_constraint(pull, pbc, xp, v, pull->bVirial && MASTER(cr), vir, dt, t);
 +}
 +
 +static void make_local_pull_group(gmx_ga2la_t ga2la,
 +                                  t_pull_group *pg, int start, int end)
 +{
 +    int i, ii;
 +
 +    pg->nat_loc = 0;
 +    for (i = 0; i < pg->nat; i++)
 +    {
 +        ii = pg->ind[i];
 +        if (ga2la)
 +        {
 +            if (!ga2la_get_home(ga2la, ii, &ii))
 +            {
 +                ii = -1;
 +            }
 +        }
 +        if (ii >= start && ii < end)
 +        {
 +            /* This is a home atom, add it to the local pull group */
 +            if (pg->nat_loc >= pg->nalloc_loc)
 +            {
 +                pg->nalloc_loc = over_alloc_dd(pg->nat_loc+1);
 +                srenew(pg->ind_loc, pg->nalloc_loc);
 +                if (pg->epgrppbc == epgrppbcCOS || pg->weight)
 +                {
 +                    srenew(pg->weight_loc, pg->nalloc_loc);
 +                }
 +            }
 +            pg->ind_loc[pg->nat_loc] = ii;
 +            if (pg->weight)
 +            {
 +                pg->weight_loc[pg->nat_loc] = pg->weight[i];
 +            }
 +            pg->nat_loc++;
 +        }
 +    }
 +}
 +
 +void dd_make_local_pull_groups(gmx_domdec_t *dd, t_pull *pull, t_mdatoms *md)
 +{
 +    gmx_ga2la_t ga2la;
 +    int         g;
 +
 +    if (dd)
 +    {
 +        ga2la = dd->ga2la;
 +    }
 +    else
 +    {
 +        ga2la = NULL;
 +    }
 +
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        make_local_pull_group(ga2la, &pull->group[g],
 +                              0, md->homenr);
 +    }
 +}
 +
 +static void init_pull_group_index(FILE *fplog, t_commrec *cr,
 +                                  int g, t_pull_group *pg, ivec pulldims,
 +                                  gmx_mtop_t *mtop, t_inputrec *ir, real lambda)
 +{
 +    int                   i, ii, d, nfrozen, ndim;
 +    real                  m, w, mbd;
 +    double                tmass, wmass, wwmass;
 +    gmx_groups_t         *groups;
 +    gmx_mtop_atomlookup_t alook;
 +    t_atom               *atom;
 +
 +    if (EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +    {
 +        /* There are no masses in the integrator.
 +         * But we still want to have the correct mass-weighted COMs.
 +         * So we store the real masses in the weights.
 +         * We do not set nweight, so these weights do not end up in the tpx file.
 +         */
 +        if (pg->nweight == 0)
 +        {
 +            snew(pg->weight, pg->nat);
 +        }
 +    }
 +
 +    if (cr && PAR(cr))
 +    {
 +        pg->nat_loc    = 0;
 +        pg->nalloc_loc = 0;
 +        pg->ind_loc    = NULL;
 +        pg->weight_loc = NULL;
 +    }
 +    else
 +    {
 +        pg->nat_loc = pg->nat;
 +        pg->ind_loc = pg->ind;
 +        if (pg->epgrppbc == epgrppbcCOS)
 +        {
 +            snew(pg->weight_loc, pg->nat);
 +        }
 +        else
 +        {
 +            pg->weight_loc = pg->weight;
 +        }
 +    }
 +
 +    groups = &mtop->groups;
 +
 +    alook = gmx_mtop_atomlookup_init(mtop);
 +
 +    nfrozen = 0;
 +    tmass   = 0;
 +    wmass   = 0;
 +    wwmass  = 0;
 +    for (i = 0; i < pg->nat; i++)
 +    {
 +        ii = pg->ind[i];
 +        gmx_mtop_atomnr_to_atom(alook, ii, &atom);
 +        if (ir->opts.nFreeze)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (pulldims[d] && ir->opts.nFreeze[ggrpnr(groups, egcFREEZE, ii)][d])
 +                {
 +                    nfrozen++;
 +                }
 +            }
 +        }
 +        if (ir->efep == efepNO)
 +        {
 +            m = atom->m;
 +        }
 +        else
 +        {
 +            m = (1 - lambda)*atom->m + lambda*atom->mB;
 +        }
 +        if (pg->nweight > 0)
 +        {
 +            w = pg->weight[i];
 +        }
 +        else
 +        {
 +            w = 1;
 +        }
 +        if (EI_ENERGY_MINIMIZATION(ir->eI))
 +        {
 +            /* Move the mass to the weight */
 +            w            *= m;
 +            m             = 1;
 +            pg->weight[i] = w;
 +        }
 +        else if (ir->eI == eiBD)
 +        {
 +            if (ir->bd_fric)
 +            {
 +                mbd = ir->bd_fric*ir->delta_t;
 +            }
 +            else
 +            {
 +                if (groups->grpnr[egcTC] == NULL)
 +                {
 +                    mbd = ir->delta_t/ir->opts.tau_t[0];
 +                }
 +                else
 +                {
 +                    mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
 +                }
 +            }
 +            w            *= m/mbd;
 +            m             = mbd;
 +            pg->weight[i] = w;
 +        }
 +        tmass  += m;
 +        wmass  += m*w;
 +        wwmass += m*w*w;
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +
 +    if (wmass == 0)
 +    {
 +        gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
 +                  pg->weight ? " weighted" : "", g);
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog,
 +                "Pull group %d: %5d atoms, mass %9.3f", g, pg->nat, tmass);
 +        if (pg->weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +        {
 +            fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
 +        }
 +        if (pg->epgrppbc == epgrppbcCOS)
 +        {
 +            fprintf(fplog, ", cosine weighting will be used");
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    if (nfrozen == 0)
 +    {
 +        /* A value > 0 signals not frozen, it is updated later */
 +        pg->invtm  = 1.0;
 +    }
 +    else
 +    {
 +        ndim = 0;
 +        for (d = 0; d < DIM; d++)
 +        {
 +            ndim += pulldims[d]*pg->nat;
 +        }
 +        if (fplog && nfrozen > 0 && nfrozen < ndim)
 +        {
 +            fprintf(fplog,
 +                    "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
 +                    "         that are subject to pulling are frozen.\n"
 +                    "         For pulling the whole group will be frozen.\n\n",
 +                    g);
 +        }
 +        pg->invtm  = 0.0;
 +        pg->wscale = 1.0;
 +    }
 +}
 +
 +void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
 +               gmx_mtop_t *mtop, t_commrec *cr, const output_env_t oenv, real lambda,
 +               gmx_bool bOutFile, unsigned long Flags)
 +{
 +    t_pull       *pull;
 +    t_pull_group *pgrp;
 +    int           c, g, start = 0, end = 0, m;
 +
 +    pull = ir->pull;
 +
 +    pull->ePBC = ir->ePBC;
 +    switch (pull->ePBC)
 +    {
 +        case epbcNONE: pull->npbcdim = 0; break;
 +        case epbcXY:   pull->npbcdim = 2; break;
 +        default:       pull->npbcdim = 3; break;
 +    }
 +
 +    if (fplog)
 +    {
 +        gmx_bool bAbs, bCos;
 +
 +        bAbs = FALSE;
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (pull->group[pull->coord[c].group[0]].nat == 0 ||
 +                pull->group[pull->coord[c].group[1]].nat == 0)
 +            {
 +                bAbs = TRUE;
 +            }
 +        }
 +
 +        fprintf(fplog, "\nWill apply %s COM pulling in geometry '%s'\n",
 +                EPULLTYPE(ir->ePull), EPULLGEOM(pull->eGeom));
 +        fprintf(fplog, "with %d pull coordinate%s and %d group%s\n",
 +                pull->ncoord, pull->ncoord == 1 ? "" : "s",
 +                pull->ngroup, pull->ngroup == 1 ? "" : "s");
 +        if (bAbs)
 +        {
 +            fprintf(fplog, "with an absolute reference\n");
 +        }
 +        bCos = FALSE;
 +        for (g = 0; g < pull->ngroup; g++)
 +        {
 +            if (pull->group[g].nat > 1 &&
 +                pull->group[g].pbcatom < 0)
 +            {
 +                /* We are using cosine weighting */
 +                fprintf(fplog, "Cosine weighting is used for group %d\n", g);
 +                bCos = TRUE;
 +            }
 +        }
 +        if (bCos)
 +        {
 +            please_cite(fplog, "Engin2010");
 +        }
 +    }
 +
 +    /* We always add the virial contribution,
 +     * except for geometry = direction_periodic where this is impossible.
 +     */
 +    pull->bVirial = (pull->eGeom != epullgDIRPBC);
 +    if (getenv("GMX_NO_PULLVIR") != NULL)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Found env. var., will not add the virial contribution of the COM pull forces\n");
 +        }
 +        pull->bVirial = FALSE;
 +    }
 +
 +    pull->rbuf     = NULL;
 +    pull->dbuf     = NULL;
 +    pull->dbuf_cyl = NULL;
 +    pull->bRefAt   = FALSE;
 +    pull->cosdim   = -1;
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        pgrp           = &pull->group[g];
 +        pgrp->epgrppbc = epgrppbcNONE;
 +        if (pgrp->nat > 0)
 +        {
 +            /* Determine if we need to take PBC into account for calculating
 +             * the COM's of the pull groups.
 +             */
 +            for (m = 0; m < pull->npbcdim; m++)
 +            {
 +                if (pull->dim[m] && pgrp->nat > 1)
 +                {
 +                    if (pgrp->pbcatom >= 0)
 +                    {
 +                        pgrp->epgrppbc = epgrppbcREFAT;
 +                        pull->bRefAt   = TRUE;
 +                    }
 +                    else
 +                    {
 +                        if (pgrp->weight)
 +                        {
 +                            gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
 +                        }
 +                        pgrp->epgrppbc = epgrppbcCOS;
 +                        if (pull->cosdim >= 0 && pull->cosdim != m)
 +                        {
 +                            gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull_dim)");
 +                        }
 +                        pull->cosdim = m;
 +                    }
 +                }
 +            }
 +            /* Set the indices */
 +            init_pull_group_index(fplog, cr, g, pgrp, pull->dim, mtop, ir, lambda);
 +            if (PULL_CYL(pull) && pgrp->invtm == 0)
 +            {
 +                gmx_fatal(FARGS, "Can not have frozen atoms in a cylinder pull group");
 +            }
 +        }
 +        else
 +        {
 +            /* Absolute reference, set the inverse mass to zero */
 +            pgrp->invtm  = 0;
 +            pgrp->wscale = 1;
 +        }
 +    }
 +
 +    /* if we use dynamic reference groups, do some initialising for them */
 +    if (PULL_CYL(pull))
 +    {
 +        if (ir->ePull == epullCONSTRAINT && pull->ncoord > 1)
 +        {
 +            /* We can't easily update the single reference group with multiple
 +             * constraints. This would require recalculating COMs.
 +             */
 +            gmx_fatal(FARGS, "Constraint COM pulling supports only one coordinate with geometry=cylinder, you can use umbrella pulling with multiple coordinates");
 +        }
 +
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (pull->group[pull->coord[c].group[0]].nat == 0)
 +            {
 +                gmx_fatal(FARGS, "Dynamic reference groups are not supported when using absolute reference!\n");
 +            }
 +        }
 +
 +        snew(pull->dyna, pull->ncoord);
 +    }
 +
 +    /* Only do I/O when we are doing dynamics and if we are the MASTER */
 +    pull->out_x = NULL;
 +    pull->out_f = NULL;
 +    if (bOutFile)
 +    {
 +        if (pull->nstxout > 0)
 +        {
 +            pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv, TRUE, Flags);
 +        }
 +        if (pull->nstfout > 0)
 +        {
 +            pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
 +                                        FALSE, Flags);
 +        }
 +    }
 +}
 +
 +void finish_pull(t_pull *pull)
 +{
 +    if (pull->out_x)
 +    {
 +        gmx_fio_fclose(pull->out_x);
 +    }
 +    if (pull->out_f)
 +    {
 +        gmx_fio_fclose(pull->out_f);
 +    }
 +}
index e49e19b2f2cccaba65e69df6226a1b4ab8526749,0000000000000000000000000000000000000000..018daf5d0efda975b0ea601a45306acf48843f08
mode 100644,000000..100644
--- /dev/null
@@@ -1,1993 -1,0 +1,2009 @@@
 +/*
 + * 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) 2011,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 "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "sysstuff.h"
 +#include "vec.h"
 +#include "vcm.h"
 +#include "mdebin.h"
 +#include "nrnb.h"
 +#include "calcmu.h"
 +#include "index.h"
 +#include "vsite.h"
 +#include "update.h"
 +#include "ns.h"
 +#include "mdrun.h"
 +#include "md_support.h"
 +#include "md_logging.h"
 +#include "network.h"
 +#include "xvgr.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "force.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "pme.h"
 +#include "mdatoms.h"
 +#include "repl_ex.h"
 +#include "deform.h"
 +#include "qmmm.h"
 +#include "domdec.h"
 +#include "domdec_network.h"
 +#include "gromacs/gmxlib/topsort.h"
 +#include "coulomb.h"
 +#include "constr.h"
 +#include "shellfc.h"
 +#include "gromacs/gmxpreprocess/compute_io.h"
 +#include "checkpoint.h"
 +#include "mtop_util.h"
 +#include "sighandler.h"
 +#include "txtdump.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "pme_loadbal.h"
 +#include "bondf.h"
 +#include "membed.h"
 +#include "types/nlistheuristics.h"
 +#include "types/iteratedconstraints.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/trajectory_writing.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/fileio/xtcio.h"
 +#include "gromacs/timing/wallcycle.h"
 +#include "gromacs/timing/walltime_accounting.h"
 +#include "gromacs/pulling/pull.h"
 +#include "gromacs/swap/swapcoords.h"
 +#include "gromacs/imd/imd.h"
 +
 +#ifdef GMX_FAHCORE
 +#include "corewrap.h"
 +#endif
 +
 +static void reset_all_counters(FILE *fplog, t_commrec *cr,
 +                               gmx_int64_t step,
 +                               gmx_int64_t *step_rel, t_inputrec *ir,
 +                               gmx_wallcycle_t wcycle, t_nrnb *nrnb,
 +                               gmx_walltime_accounting_t walltime_accounting,
 +                               nbnxn_cuda_ptr_t cu_nbv)
 +{
 +    char sbuf[STEPSTRSIZE];
 +
 +    /* Reset all the counters related to performance over the run */
 +    md_print_warn(cr, fplog, "step %s: resetting all time and cycle counters\n",
 +                  gmx_step_str(step, sbuf));
 +
 +    if (cu_nbv)
 +    {
 +        nbnxn_cuda_reset_timings(cu_nbv);
 +    }
 +
 +    wallcycle_stop(wcycle, ewcRUN);
 +    wallcycle_reset_all(wcycle);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        reset_dd_statistics_counters(cr->dd);
 +    }
 +    init_nrnb(nrnb);
 +    ir->init_step += *step_rel;
 +    ir->nsteps    -= *step_rel;
 +    *step_rel      = 0;
 +    wallcycle_start(wcycle, ewcRUN);
 +    walltime_accounting_start(walltime_accounting);
 +    print_date_and_time(fplog, cr->nodeid, "Restarted time", gmx_gettime());
 +}
 +
 +double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
 +             const output_env_t oenv, gmx_bool bVerbose, gmx_bool bCompact,
 +             int nstglobalcomm,
 +             gmx_vsite_t *vsite, gmx_constr_t constr,
 +             int stepout, t_inputrec *ir,
 +             gmx_mtop_t *top_global,
 +             t_fcdata *fcd,
 +             t_state *state_global,
 +             t_mdatoms *mdatoms,
 +             t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +             gmx_edsam_t ed, t_forcerec *fr,
 +             int repl_ex_nst, int repl_ex_nex, int repl_ex_seed, gmx_membed_t membed,
 +             real cpt_period, real max_hours,
 +             const char gmx_unused *deviceOptions,
 +             int imdport,
 +             unsigned long Flags,
 +             gmx_walltime_accounting_t walltime_accounting)
 +{
 +    gmx_mdoutf_t    outf = NULL;
 +    gmx_int64_t     step, step_rel;
 +    double          elapsed_time;
 +    double          t, t0, lam0[efptNR];
 +    gmx_bool        bGStatEveryStep, bGStat, bCalcVir, bCalcEner;
 +    gmx_bool        bNS, bNStList, bSimAnn, bStopCM, bRerunMD, bNotLastFrame = FALSE,
 +                    bFirstStep, bStateFromCP, bStateFromTPX, bInitStep, bLastStep,
 +                    bBornRadii, bStartingFromCpt;
 +    gmx_bool          bDoDHDL = FALSE, bDoFEP = FALSE, bDoExpanded = FALSE;
 +    gmx_bool          do_ene, do_log, do_verbose, bRerunWarnNoV = TRUE,
 +                      bForceUpdate = FALSE, bCPT;
 +    gmx_bool          bMasterState;
 +    int               force_flags, cglo_flags;
 +    tensor            force_vir, shake_vir, total_vir, tmp_vir, pres;
 +    int               i, m;
 +    t_trxstatus      *status;
 +    rvec              mu_tot;
 +    t_vcm            *vcm;
 +    t_state          *bufstate = NULL;
 +    matrix           *scale_tot, pcoupl_mu, M, ebox;
 +    gmx_nlheur_t      nlh;
 +    t_trxframe        rerun_fr;
 +    gmx_repl_ex_t     repl_ex = NULL;
 +    int               nchkpt  = 1;
 +    gmx_localtop_t   *top;
 +    t_mdebin         *mdebin   = NULL;
 +    t_state          *state    = NULL;
 +    rvec             *f_global = NULL;
 +    gmx_enerdata_t   *enerd;
 +    rvec             *f = NULL;
 +    gmx_global_stat_t gstat;
 +    gmx_update_t      upd   = NULL;
 +    t_graph          *graph = NULL;
 +    globsig_t         gs;
 +    gmx_groups_t     *groups;
 +    gmx_ekindata_t   *ekind, *ekind_save;
 +    gmx_shellfc_t     shellfc;
 +    int               count, nconverged = 0;
 +    real              timestep   = 0;
 +    double            tcount     = 0;
 +    gmx_bool          bConverged = TRUE, bOK, bSumEkinhOld, bExchanged, bNeedRepartition;
 +    gmx_bool          bAppend;
 +    gmx_bool          bResetCountersHalfMaxH = FALSE;
 +    gmx_bool          bVV, bIterativeCase, bFirstIterate, bTemp, bPres, bTrotter;
 +    gmx_bool          bUpdateDoLR;
 +    real              dvdl_constr;
 +    rvec             *cbuf = NULL;
 +    matrix            lastbox;
 +    real              veta_save, scalevir, tracevir;
 +    real              vetanew = 0;
 +    int               lamnew  = 0;
 +    /* for FEP */
 +    int               nstfep;
 +    double            cycles;
 +    real              saved_conserved_quantity = 0;
 +    real              last_ekin                = 0;
 +    int               iter_i;
 +    t_extmass         MassQ;
 +    int             **trotter_seq;
 +    char              sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE];
 +    int               handled_stop_condition = gmx_stop_cond_none; /* compare to get_stop_condition*/
 +    gmx_iterate_t     iterate;
 +    gmx_int64_t       multisim_nsteps = -1;                        /* number of steps to do  before first multisim
 +                                                                          simulation stops. If equal to zero, don't
 +                                                                          communicate any more between multisims.*/
 +    /* PME load balancing data for GPU kernels */
 +    pme_load_balancing_t pme_loadbal = NULL;
 +    double               cycles_pmes;
 +    gmx_bool             bPMETuneTry = FALSE, bPMETuneRunning = FALSE;
 +
 +    /* Interactive MD */
 +    gmx_bool          bIMDstep = FALSE;
 +
 +#ifdef GMX_FAHCORE
 +    /* Temporary addition for FAHCORE checkpointing */
 +    int chkpt_ret;
 +#endif
 +
 +    /* Check for special mdrun options */
 +    bRerunMD = (Flags & MD_RERUN);
 +    bAppend  = (Flags & MD_APPENDFILES);
 +    if (Flags & MD_RESETCOUNTERSHALFWAY)
 +    {
 +        if (ir->nsteps > 0)
 +        {
 +            /* Signal to reset the counters half the simulation steps. */
 +            wcycle_set_reset_counters(wcycle, ir->nsteps/2);
 +        }
 +        /* Signal to reset the counters halfway the simulation time. */
 +        bResetCountersHalfMaxH = (max_hours > 0);
 +    }
 +
 +    /* md-vv uses averaged full step velocities for T-control
 +       md-vv-avek uses averaged half step velocities for T-control (but full step ekin for P control)
 +       md uses averaged half step kinetic energies to determine temperature unless defined otherwise by GMX_EKIN_AVE_VEL; */
 +    bVV = EI_VV(ir->eI);
 +    if (bVV) /* to store the initial velocities while computing virial */
 +    {
 +        snew(cbuf, top_global->natoms);
 +    }
 +    /* all the iteratative cases - only if there are constraints */
 +    bIterativeCase = ((IR_NPH_TROTTER(ir) || IR_NPT_TROTTER(ir)) && (constr) && (!bRerunMD));
 +    gmx_iterate_init(&iterate, FALSE); /* The default value of iterate->bIterationActive is set to
 +                                          false in this step.  The correct value, true or false,
 +                                          is set at each step, as it depends on the frequency of temperature
 +                                          and pressure control.*/
 +    bTrotter = (bVV && (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir) || IR_NVT_TROTTER(ir)));
 +
 +    if (bRerunMD)
 +    {
 +        /* Since we don't know if the frames read are related in any way,
 +         * rebuild the neighborlist at every step.
 +         */
 +        ir->nstlist       = 1;
 +        ir->nstcalcenergy = 1;
 +        nstglobalcomm     = 1;
 +    }
 +
 +    check_ir_old_tpx_versions(cr, fplog, ir, top_global);
 +
 +    nstglobalcomm   = check_nstglobalcomm(fplog, cr, nstglobalcomm, ir);
 +    bGStatEveryStep = (nstglobalcomm == 1);
 +
 +    if (!bGStatEveryStep && ir->nstlist == -1 && fplog != NULL)
 +    {
 +        fprintf(fplog,
 +                "To reduce the energy communication with nstlist = -1\n"
 +                "the neighbor list validity should not be checked at every step,\n"
 +                "this means that exact integration is not guaranteed.\n"
 +                "The neighbor list validity is checked after:\n"
 +                "  <n.list life time> - 2*std.dev.(n.list life time)  steps.\n"
 +                "In most cases this will result in exact integration.\n"
 +                "This reduces the energy communication by a factor of 2 to 3.\n"
 +                "If you want less energy communication, set nstlist > 3.\n\n");
 +    }
 +
 +    if (bRerunMD)
 +    {
 +        ir->nstxout_compressed = 0;
 +    }
 +    groups = &top_global->groups;
 +
 +    /* Initial values */
 +    init_md(fplog, cr, ir, oenv, &t, &t0, state_global->lambda,
 +            &(state_global->fep_state), lam0,
 +            nrnb, top_global, &upd,
 +            nfile, fnm, &outf, &mdebin,
 +            force_vir, shake_vir, mu_tot, &bSimAnn, &vcm, Flags);
 +
 +    clear_mat(total_vir);
 +    clear_mat(pres);
 +    /* Energy terms and groups */
 +    snew(enerd, 1);
 +    init_enerdata(top_global->groups.grps[egcENER].nr, ir->fepvals->n_lambda,
 +                  enerd);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        f = NULL;
 +    }
 +    else
 +    {
 +        snew(f, top_global->natoms);
 +    }
 +
 +    /* Kinetic energy data */
 +    snew(ekind, 1);
 +    init_ekindata(fplog, top_global, &(ir->opts), ekind);
 +    /* needed for iteration of constraints */
 +    snew(ekind_save, 1);
 +    init_ekindata(fplog, top_global, &(ir->opts), ekind_save);
 +    /* Copy the cos acceleration to the groups struct */
 +    ekind->cosacc.cos_accel = ir->cos_accel;
 +
 +    gstat = global_stat_init(ir);
 +    debug_gmx();
 +
 +    /* Check for polarizable models and flexible constraints */
 +    shellfc = init_shell_flexcon(fplog, fr->cutoff_scheme == ecutsVERLET,
 +                                 top_global, n_flexible_constraints(constr),
 +                                 (ir->bContinuation ||
 +                                  (DOMAINDECOMP(cr) && !MASTER(cr))) ?
 +                                 NULL : state_global->x);
 +
++    if (shellfc && ir->eI == eiNM)
++    {
++        /* Currently shells don't work with Normal Modes */
++        gmx_fatal(FARGS, "Normal Mode analysis is not supported with shells.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
++    }
++
++    if (vsite && ir->eI == eiNM)
++    {
++        /* Currently virtual sites don't work with Normal Modes */
++        gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
++    }
++
 +    if (DEFORM(*ir))
 +    {
 +        tMPI_Thread_mutex_lock(&deform_init_box_mutex);
 +        set_deform_reference_box(upd,
 +                                 deform_init_init_step_tpx,
 +                                 deform_init_box_tpx);
 +        tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
 +    }
 +
 +    {
 +        double io = compute_io(ir, top_global->natoms, groups, mdebin->ebin->nener, 1);
 +        if ((io > 2000) && MASTER(cr))
 +        {
 +            fprintf(stderr,
 +                    "\nWARNING: This run will generate roughly %.0f Mb of data\n\n",
 +                    io);
 +        }
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        top = dd_init_local_top(top_global);
 +
 +        snew(state, 1);
 +        dd_init_local_state(cr->dd, state_global, state);
 +
 +        if (DDMASTER(cr->dd) && ir->nstfout)
 +        {
 +            snew(f_global, state_global->natoms);
 +        }
 +    }
 +    else
 +    {
 +        top = gmx_mtop_generate_local_top(top_global, ir);
 +
 +        forcerec_set_excl_load(fr, top);
 +
 +        state    = serial_init_local_state(state_global);
 +        f_global = f;
 +
 +        atoms2md(top_global, ir, 0, NULL, top_global->natoms, mdatoms);
 +
 +        if (vsite)
 +        {
 +            set_vsite_top(vsite, top, mdatoms, cr);
 +        }
 +
 +        if (ir->ePBC != epbcNONE && !fr->bMolPBC)
 +        {
 +            graph = mk_graph(fplog, &(top->idef), 0, top_global->natoms, FALSE, FALSE);
 +        }
 +
 +        if (shellfc)
 +        {
 +            make_local_shells(cr, mdatoms, shellfc);
 +        }
 +
 +        setup_bonded_threading(fr, &top->idef);
 +    }
 +
 +    /* Set up interactive MD (IMD) */
 +    init_IMD(ir, cr, top_global, fplog, ir->nstcalcenergy, state_global->x,
 +             nfile, fnm, oenv, imdport, Flags);
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        /* Distribute the charge groups over the nodes from the master node */
 +        dd_partition_system(fplog, ir->init_step, cr, TRUE, 1,
 +                            state_global, top_global, ir,
 +                            state, &f, mdatoms, top, fr,
 +                            vsite, shellfc, constr,
 +                            nrnb, wcycle, FALSE);
 +
 +    }
 +
 +    update_mdatoms(mdatoms, state->lambda[efptMASS]);
 +
 +    if (opt2bSet("-cpi", nfile, fnm))
 +    {
 +        bStateFromCP = gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr);
 +    }
 +    else
 +    {
 +        bStateFromCP = FALSE;
 +    }
 +
 +    if (ir->bExpanded)
 +    {
 +        init_expanded_ensemble(bStateFromCP, ir, &state->dfhist);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        if (bStateFromCP)
 +        {
 +            /* Update mdebin with energy history if appending to output files */
 +            if (Flags & MD_APPENDFILES)
 +            {
 +                restore_energyhistory_from_state(mdebin, &state_global->enerhist);
 +            }
 +            else
 +            {
 +                /* We might have read an energy history from checkpoint,
 +                 * free the allocated memory and reset the counts.
 +                 */
 +                done_energyhistory(&state_global->enerhist);
 +                init_energyhistory(&state_global->enerhist);
 +            }
 +        }
 +        /* Set the initial energy history in state by updating once */
 +        update_energyhistory(&state_global->enerhist, mdebin);
 +    }
 +
 +    /* Initialize constraints */
 +    if (constr && !DOMAINDECOMP(cr))
 +    {
 +        set_constraints(constr, top, ir, mdatoms, cr);
 +    }
 +
 +    if (repl_ex_nst > 0)
 +    {
 +        /* We need to be sure replica exchange can only occur
 +         * when the energies are current */
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "repl_ex_nst", &repl_ex_nst);
 +        /* This check needs to happen before inter-simulation
 +         * signals are initialized, too */
 +    }
 +    if (repl_ex_nst > 0 && MASTER(cr))
 +    {
 +        repl_ex = init_replica_exchange(fplog, cr->ms, state_global, ir,
 +                                        repl_ex_nst, repl_ex_nex, repl_ex_seed);
 +    }
 +
 +    /* PME tuning is only supported with GPUs or PME nodes and not with rerun.
 +     * PME tuning is not supported with PME only for LJ and not for Coulomb.
 +     */
 +    if ((Flags & MD_TUNEPME) &&
 +        EEL_PME(fr->eeltype) &&
 +        ( (fr->cutoff_scheme == ecutsVERLET && fr->nbv->bUseGPU) || !(cr->duty & DUTY_PME)) &&
 +        !bRerunMD)
 +    {
 +        pme_loadbal_init(&pme_loadbal, ir, state->box, fr->ic, fr->pmedata);
 +        cycles_pmes = 0;
 +        if (cr->duty & DUTY_PME)
 +        {
 +            /* Start tuning right away, as we can't measure the load */
 +            bPMETuneRunning = TRUE;
 +        }
 +        else
 +        {
 +            /* Separate PME nodes, we can measure the PP/PME load balance */
 +            bPMETuneTry = TRUE;
 +        }
 +    }
 +
 +    if (!ir->bContinuation && !bRerunMD)
 +    {
 +        if (mdatoms->cFREEZE && (state->flags & (1<<estV)))
 +        {
 +            /* Set the velocities of frozen particles to zero */
 +            for (i = 0; i < mdatoms->homenr; i++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (ir->opts.nFreeze[mdatoms->cFREEZE[i]][m])
 +                    {
 +                        state->v[i][m] = 0;
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (constr)
 +        {
 +            /* Constrain the initial coordinates and velocities */
 +            do_constrain_first(fplog, constr, ir, mdatoms, state,
 +                               cr, nrnb, fr, top);
 +        }
 +        if (vsite)
 +        {
 +            /* Construct the virtual sites for the initial configuration */
 +            construct_vsites(vsite, state->x, ir->delta_t, NULL,
 +                             top->idef.iparams, top->idef.il,
 +                             fr->ePBC, fr->bMolPBC, cr, state->box);
 +        }
 +    }
 +
 +    debug_gmx();
 +
 +    /* set free energy calculation frequency as the minimum
 +       greatest common denominator of nstdhdl, nstexpanded, and repl_ex_nst*/
 +    nstfep = ir->fepvals->nstdhdl;
 +    if (ir->bExpanded)
 +    {
 +        nstfep = gmx_greatest_common_divisor(ir->fepvals->nstdhdl, nstfep);
 +    }
 +    if (repl_ex_nst > 0)
 +    {
 +        nstfep = gmx_greatest_common_divisor(repl_ex_nst, nstfep);
 +    }
 +
 +    /* I'm assuming we need global communication the first time! MRS */
 +    cglo_flags = (CGLO_TEMPERATURE | CGLO_GSTAT
 +                  | ((ir->comm_mode != ecmNO) ? CGLO_STOPCM : 0)
 +                  | (bVV ? CGLO_PRESSURE : 0)
 +                  | (bVV ? CGLO_CONSTRAINT : 0)
 +                  | (bRerunMD ? CGLO_RERUNMD : 0)
 +                  | ((Flags & MD_READ_EKIN) ? CGLO_READEKIN : 0));
 +
 +    bSumEkinhOld = FALSE;
 +    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                    NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                    constr, NULL, FALSE, state->box,
 +                    top_global, &bSumEkinhOld, cglo_flags);
 +    if (ir->eI == eiVVAK)
 +    {
 +        /* a second call to get the half step temperature initialized as well */
 +        /* we do the same call as above, but turn the pressure off -- internally to
 +           compute_globals, this is recognized as a velocity verlet half-step
 +           kinetic energy calculation.  This minimized excess variables, but
 +           perhaps loses some logic?*/
 +
 +        compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                        NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                        constr, NULL, FALSE, state->box,
 +                        top_global, &bSumEkinhOld,
 +                        cglo_flags &~(CGLO_STOPCM | CGLO_PRESSURE));
 +    }
 +
 +    /* Calculate the initial half step temperature, and save the ekinh_old */
 +    if (!(Flags & MD_STARTFROMCPT))
 +    {
 +        for (i = 0; (i < ir->opts.ngtc); i++)
 +        {
 +            copy_mat(ekind->tcstat[i].ekinh, ekind->tcstat[i].ekinh_old);
 +        }
 +    }
 +    if (ir->eI != eiVV)
 +    {
 +        enerd->term[F_TEMP] *= 2; /* result of averages being done over previous and current step,
 +                                     and there is no previous step */
 +    }
 +
 +    /* if using an iterative algorithm, we need to create a working directory for the state. */
 +    if (bIterativeCase)
 +    {
 +        bufstate = init_bufstate(state);
 +    }
 +
 +    /* need to make an initiation call to get the Trotter variables set, as well as other constants for non-trotter
 +       temperature control */
 +    trotter_seq = init_npt_vars(ir, state, &MassQ, bTrotter);
 +
 +    if (MASTER(cr))
 +    {
 +        if (constr && !ir->bContinuation && ir->eConstrAlg == econtLINCS)
 +        {
 +            fprintf(fplog,
 +                    "RMS relative constraint deviation after constraining: %.2e\n",
 +                    constr_rmsd(constr, FALSE));
 +        }
 +        if (EI_STATE_VELOCITY(ir->eI))
 +        {
 +            fprintf(fplog, "Initial temperature: %g K\n", enerd->term[F_TEMP]);
 +        }
 +        if (bRerunMD)
 +        {
 +            fprintf(stderr, "starting md rerun '%s', reading coordinates from"
 +                    " input trajectory '%s'\n\n",
 +                    *(top_global->name), opt2fn("-rerun", nfile, fnm));
 +            if (bVerbose)
 +            {
 +                fprintf(stderr, "Calculated time to finish depends on nsteps from "
 +                        "run input file,\nwhich may not correspond to the time "
 +                        "needed to process input trajectory.\n\n");
 +            }
 +        }
 +        else
 +        {
 +            char tbuf[20];
 +            fprintf(stderr, "starting mdrun '%s'\n",
 +                    *(top_global->name));
 +            if (ir->nsteps >= 0)
 +            {
 +                sprintf(tbuf, "%8.1f", (ir->init_step+ir->nsteps)*ir->delta_t);
 +            }
 +            else
 +            {
 +                sprintf(tbuf, "%s", "infinite");
 +            }
 +            if (ir->init_step > 0)
 +            {
 +                fprintf(stderr, "%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
 +                        gmx_step_str(ir->init_step+ir->nsteps, sbuf), tbuf,
 +                        gmx_step_str(ir->init_step, sbuf2),
 +                        ir->init_step*ir->delta_t);
 +            }
 +            else
 +            {
 +                fprintf(stderr, "%s steps, %s ps.\n",
 +                        gmx_step_str(ir->nsteps, sbuf), tbuf);
 +            }
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    walltime_accounting_start(walltime_accounting);
 +    wallcycle_start(wcycle, ewcRUN);
 +    print_start(fplog, cr, walltime_accounting, "mdrun");
 +
 +    /* safest point to do file checkpointing is here.  More general point would be immediately before integrator call */
 +#ifdef GMX_FAHCORE
 +    chkpt_ret = fcCheckPointParallel( cr->nodeid,
 +                                      NULL, 0);
 +    if (chkpt_ret == 0)
 +    {
 +        gmx_fatal( 3, __FILE__, __LINE__, "Checkpoint error on step %d\n", 0 );
 +    }
 +#endif
 +
 +    debug_gmx();
 +    /***********************************************************
 +     *
 +     *             Loop over MD steps
 +     *
 +     ************************************************************/
 +
 +    /* if rerunMD then read coordinates and velocities from input trajectory */
 +    if (bRerunMD)
 +    {
 +        if (getenv("GMX_FORCE_UPDATE"))
 +        {
 +            bForceUpdate = TRUE;
 +        }
 +
 +        rerun_fr.natoms = 0;
 +        if (MASTER(cr))
 +        {
 +            bNotLastFrame = read_first_frame(oenv, &status,
 +                                             opt2fn("-rerun", nfile, fnm),
 +                                             &rerun_fr, TRX_NEED_X | TRX_READ_V);
 +            if (rerun_fr.natoms != top_global->natoms)
 +            {
 +                gmx_fatal(FARGS,
 +                          "Number of atoms in trajectory (%d) does not match the "
 +                          "run input file (%d)\n",
 +                          rerun_fr.natoms, top_global->natoms);
 +            }
 +            if (ir->ePBC != epbcNONE)
 +            {
 +                if (!rerun_fr.bBox)
 +                {
 +                    gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f does not contain a box, while pbc is used", rerun_fr.step, rerun_fr.time);
 +                }
 +                if (max_cutoff2(ir->ePBC, rerun_fr.box) < sqr(fr->rlistlong))
 +                {
 +                    gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f has too small box dimensions", rerun_fr.step, rerun_fr.time);
 +                }
 +            }
 +        }
 +
 +        if (PAR(cr))
 +        {
 +            rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame);
 +        }
 +
 +        if (ir->ePBC != epbcNONE)
 +        {
 +            /* Set the shift vectors.
 +             * Necessary here when have a static box different from the tpr box.
 +             */
 +            calc_shifts(rerun_fr.box, fr->shift_vec);
 +        }
 +    }
 +
 +    /* loop over MD steps or if rerunMD to end of input trajectory */
 +    bFirstStep = TRUE;
 +    /* Skip the first Nose-Hoover integration when we get the state from tpx */
 +    bStateFromTPX    = !bStateFromCP;
 +    bInitStep        = bFirstStep && (bStateFromTPX || bVV);
 +    bStartingFromCpt = (Flags & MD_STARTFROMCPT) && bInitStep;
 +    bLastStep        = FALSE;
 +    bSumEkinhOld     = FALSE;
 +    bExchanged       = FALSE;
 +    bNeedRepartition = FALSE;
 +
 +    init_global_signals(&gs, cr, ir, repl_ex_nst);
 +
 +    step     = ir->init_step;
 +    step_rel = 0;
 +
 +    if (ir->nstlist == -1)
 +    {
 +        init_nlistheuristics(&nlh, bGStatEveryStep, step);
 +    }
 +
 +    if (MULTISIM(cr) && (repl_ex_nst <= 0 ))
 +    {
 +        /* check how many steps are left in other sims */
 +        multisim_nsteps = get_multisim_nsteps(cr, ir->nsteps);
 +    }
 +
 +
 +    /* and stop now if we should */
 +    bLastStep = (bRerunMD || (ir->nsteps >= 0 && step_rel > ir->nsteps) ||
 +                 ((multisim_nsteps >= 0) && (step_rel >= multisim_nsteps )));
 +    while (!bLastStep || (bRerunMD && bNotLastFrame))
 +    {
 +
 +        wallcycle_start(wcycle, ewcSTEP);
 +
 +        if (bRerunMD)
 +        {
 +            if (rerun_fr.bStep)
 +            {
 +                step     = rerun_fr.step;
 +                step_rel = step - ir->init_step;
 +            }
 +            if (rerun_fr.bTime)
 +            {
 +                t = rerun_fr.time;
 +            }
 +            else
 +            {
 +                t = step;
 +            }
 +        }
 +        else
 +        {
 +            bLastStep = (step_rel == ir->nsteps);
 +            t         = t0 + step*ir->delta_t;
 +        }
 +
 +        if (ir->efep != efepNO || ir->bSimTemp)
 +        {
 +            /* find and set the current lambdas.  If rerunning, we either read in a state, or a lambda value,
 +               requiring different logic. */
 +
 +            set_current_lambdas(step, ir->fepvals, bRerunMD, &rerun_fr, state_global, state, lam0);
 +            bDoDHDL      = do_per_step(step, ir->fepvals->nstdhdl);
 +            bDoFEP       = (do_per_step(step, nstfep) && (ir->efep != efepNO));
 +            bDoExpanded  = (do_per_step(step, ir->expandedvals->nstexpanded)
 +                            && (ir->bExpanded) && (step > 0) && (!bStartingFromCpt));
 +        }
 +
 +        if (bSimAnn)
 +        {
 +            update_annealing_target_temp(&(ir->opts), t);
 +        }
 +
 +        if (bRerunMD)
 +        {
 +            if (!DOMAINDECOMP(cr) || MASTER(cr))
 +            {
 +                for (i = 0; i < state_global->natoms; i++)
 +                {
 +                    copy_rvec(rerun_fr.x[i], state_global->x[i]);
 +                }
 +                if (rerun_fr.bV)
 +                {
 +                    for (i = 0; i < state_global->natoms; i++)
 +                    {
 +                        copy_rvec(rerun_fr.v[i], state_global->v[i]);
 +                    }
 +                }
 +                else
 +                {
 +                    for (i = 0; i < state_global->natoms; i++)
 +                    {
 +                        clear_rvec(state_global->v[i]);
 +                    }
 +                    if (bRerunWarnNoV)
 +                    {
 +                        fprintf(stderr, "\nWARNING: Some frames do not contain velocities.\n"
 +                                "         Ekin, temperature and pressure are incorrect,\n"
 +                                "         the virial will be incorrect when constraints are present.\n"
 +                                "\n");
 +                        bRerunWarnNoV = FALSE;
 +                    }
 +                }
 +            }
 +            copy_mat(rerun_fr.box, state_global->box);
 +            copy_mat(state_global->box, state->box);
 +
 +            if (vsite && (Flags & MD_RERUN_VSITE))
 +            {
 +                if (DOMAINDECOMP(cr))
 +                {
 +                    gmx_fatal(FARGS, "Vsite recalculation with -rerun is not implemented with domain decomposition, use a single rank");
 +                }
 +                if (graph)
 +                {
 +                    /* Following is necessary because the graph may get out of sync
 +                     * with the coordinates if we only have every N'th coordinate set
 +                     */
 +                    mk_mshift(fplog, graph, fr->ePBC, state->box, state->x);
 +                    shift_self(graph, state->box, state->x);
 +                }
 +                construct_vsites(vsite, state->x, ir->delta_t, state->v,
 +                                 top->idef.iparams, top->idef.il,
 +                                 fr->ePBC, fr->bMolPBC, cr, state->box);
 +                if (graph)
 +                {
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +            }
 +        }
 +
 +        /* Stop Center of Mass motion */
 +        bStopCM = (ir->comm_mode != ecmNO && do_per_step(step, ir->nstcomm));
 +
 +        if (bRerunMD)
 +        {
 +            /* for rerun MD always do Neighbour Searching */
 +            bNS      = (bFirstStep || ir->nstlist != 0);
 +            bNStList = bNS;
 +        }
 +        else
 +        {
 +            /* Determine whether or not to do Neighbour Searching and LR */
 +            bNStList = (ir->nstlist > 0  && step % ir->nstlist == 0);
 +
 +            bNS = (bFirstStep || bExchanged || bNeedRepartition || bNStList || bDoFEP ||
 +                   (ir->nstlist == -1 && nlh.nabnsb > 0));
 +
 +            if (bNS && ir->nstlist == -1)
 +            {
 +                set_nlistheuristics(&nlh, bFirstStep || bExchanged || bNeedRepartition || bDoFEP, step);
 +            }
 +        }
 +
 +        /* check whether we should stop because another simulation has
 +           stopped. */
 +        if (MULTISIM(cr))
 +        {
 +            if ( (multisim_nsteps >= 0) &&  (step_rel >= multisim_nsteps)  &&
 +                 (multisim_nsteps != ir->nsteps) )
 +            {
 +                if (bNS)
 +                {
 +                    if (MASTER(cr))
 +                    {
 +                        fprintf(stderr,
 +                                "Stopping simulation %d because another one has finished\n",
 +                                cr->ms->sim);
 +                    }
 +                    bLastStep         = TRUE;
 +                    gs.sig[eglsCHKPT] = 1;
 +                }
 +            }
 +        }
 +
 +        /* < 0 means stop at next step, > 0 means stop at next NS step */
 +        if ( (gs.set[eglsSTOPCOND] < 0) ||
 +             ( (gs.set[eglsSTOPCOND] > 0) && (bNStList || ir->nstlist == 0) ) )
 +        {
 +            bLastStep = TRUE;
 +        }
 +
 +        /* Determine whether or not to update the Born radii if doing GB */
 +        bBornRadii = bFirstStep;
 +        if (ir->implicit_solvent && (step % ir->nstgbradii == 0))
 +        {
 +            bBornRadii = TRUE;
 +        }
 +
 +        do_log     = do_per_step(step, ir->nstlog) || bFirstStep || bLastStep;
 +        do_verbose = bVerbose &&
 +            (step % stepout == 0 || bFirstStep || bLastStep);
 +
 +        if (bNS && !(bFirstStep && ir->bContinuation && !bRerunMD))
 +        {
 +            if (bRerunMD)
 +            {
 +                bMasterState = TRUE;
 +            }
 +            else
 +            {
 +                bMasterState = FALSE;
 +                /* Correct the new box if it is too skewed */
 +                if (DYNAMIC_BOX(*ir))
 +                {
 +                    if (correct_box(fplog, step, state->box, graph))
 +                    {
 +                        bMasterState = TRUE;
 +                    }
 +                }
 +                if (DOMAINDECOMP(cr) && bMasterState)
 +                {
 +                    dd_collect_state(cr->dd, state, state_global);
 +                }
 +            }
 +
 +            if (DOMAINDECOMP(cr))
 +            {
 +                /* Repartition the domain decomposition */
 +                wallcycle_start(wcycle, ewcDOMDEC);
 +                dd_partition_system(fplog, step, cr,
 +                                    bMasterState, nstglobalcomm,
 +                                    state_global, top_global, ir,
 +                                    state, &f, mdatoms, top, fr,
 +                                    vsite, shellfc, constr,
 +                                    nrnb, wcycle,
 +                                    do_verbose && !bPMETuneRunning);
 +                wallcycle_stop(wcycle, ewcDOMDEC);
 +                /* If using an iterative integrator, reallocate space to match the decomposition */
 +            }
 +        }
 +
 +        if (MASTER(cr) && do_log)
 +        {
 +            print_ebin_header(fplog, step, t, state->lambda[efptFEP]); /* can we improve the information printed here? */
 +        }
 +
 +        if (ir->efep != efepNO)
 +        {
 +            update_mdatoms(mdatoms, state->lambda[efptMASS]);
 +        }
 +
 +        if ((bRerunMD && rerun_fr.bV) || bExchanged)
 +        {
 +
 +            /* We need the kinetic energy at minus the half step for determining
 +             * the full step kinetic energy and possibly for T-coupling.*/
 +            /* This may not be quite working correctly yet . . . . */
 +            compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                            wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot,
 +                            constr, NULL, FALSE, state->box,
 +                            top_global, &bSumEkinhOld,
 +                            CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
 +        }
 +        clear_mat(force_vir);
 +
 +        /* We write a checkpoint at this MD step when:
 +         * either at an NS step when we signalled through gs,
 +         * or at the last step (but not when we do not want confout),
 +         * but never at the first step or with rerun.
 +         */
 +        bCPT = (((gs.set[eglsCHKPT] && (bNS || ir->nstlist == 0)) ||
 +                 (bLastStep && (Flags & MD_CONFOUT))) &&
 +                step > ir->init_step && !bRerunMD);
 +        if (bCPT)
 +        {
 +            gs.set[eglsCHKPT] = 0;
 +        }
 +
 +        /* Determine the energy and pressure:
 +         * at nstcalcenergy steps and at energy output steps (set below).
 +         */
 +        if (EI_VV(ir->eI) && (!bInitStep))
 +        {
 +            /* for vv, the first half of the integration actually corresponds
 +               to the previous step.  bCalcEner is only required to be evaluated on the 'next' step,
 +               but the virial needs to be calculated on both the current step and the 'next' step. Future
 +               reorganization may be able to get rid of one of the bCalcVir=TRUE steps. */
 +
 +            bCalcEner = do_per_step(step-1, ir->nstcalcenergy);
 +            bCalcVir  = bCalcEner ||
 +                (ir->epc != epcNO && (do_per_step(step, ir->nstpcouple) || do_per_step(step-1, ir->nstpcouple)));
 +        }
 +        else
 +        {
 +            bCalcEner = do_per_step(step, ir->nstcalcenergy);
 +            bCalcVir  = bCalcEner ||
 +                (ir->epc != epcNO && do_per_step(step, ir->nstpcouple));
 +        }
 +
 +        /* Do we need global communication ? */
 +        bGStat = (bCalcVir || bCalcEner || bStopCM ||
 +                  do_per_step(step, nstglobalcomm) || (bVV && IR_NVT_TROTTER(ir) && do_per_step(step-1, nstglobalcomm)) ||
 +                  (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck));
 +
 +        do_ene = (do_per_step(step, ir->nstenergy) || bLastStep);
 +
 +        if (do_ene || do_log)
 +        {
 +            bCalcVir  = TRUE;
 +            bCalcEner = TRUE;
 +            bGStat    = TRUE;
 +        }
 +
 +        /* these CGLO_ options remain the same throughout the iteration */
 +        cglo_flags = ((bRerunMD ? CGLO_RERUNMD : 0) |
 +                      (bGStat ? CGLO_GSTAT : 0)
 +                      );
 +
 +        force_flags = (GMX_FORCE_STATECHANGED |
 +                       ((DYNAMIC_BOX(*ir) || bRerunMD) ? GMX_FORCE_DYNAMICBOX : 0) |
 +                       GMX_FORCE_ALLFORCES |
 +                       GMX_FORCE_SEPLRF |
 +                       (bCalcVir ? GMX_FORCE_VIRIAL : 0) |
 +                       (bCalcEner ? GMX_FORCE_ENERGY : 0) |
 +                       (bDoFEP ? GMX_FORCE_DHDL : 0)
 +                       );
 +
 +        if (fr->bTwinRange)
 +        {
 +            if (do_per_step(step, ir->nstcalclr))
 +            {
 +                force_flags |= GMX_FORCE_DO_LR;
 +            }
 +        }
 +
 +        if (shellfc)
 +        {
 +            /* Now is the time to relax the shells */
 +            count = relax_shell_flexcon(fplog, cr, bVerbose, step,
 +                                        ir, bNS, force_flags,
 +                                        top,
 +                                        constr, enerd, fcd,
 +                                        state, f, force_vir, mdatoms,
 +                                        nrnb, wcycle, graph, groups,
 +                                        shellfc, fr, bBornRadii, t, mu_tot,
 +                                        &bConverged, vsite,
 +                                        mdoutf_get_fp_field(outf));
 +            tcount += count;
 +
 +            if (bConverged)
 +            {
 +                nconverged++;
 +            }
 +        }
 +        else
 +        {
 +            /* The coordinates (x) are shifted (to get whole molecules)
 +             * in do_force.
 +             * This is parallellized as well, and does communication too.
 +             * Check comments in sim_util.c
 +             */
 +            do_force(fplog, cr, ir, step, nrnb, wcycle, top, groups,
 +                     state->box, state->x, &state->hist,
 +                     f, force_vir, mdatoms, enerd, fcd,
 +                     state->lambda, graph,
 +                     fr, vsite, mu_tot, t, mdoutf_get_fp_field(outf), ed, bBornRadii,
 +                     (bNS ? GMX_FORCE_NS : 0) | force_flags);
 +        }
 +
 +        if (bVV && !bStartingFromCpt && !bRerunMD)
 +        /*  ############### START FIRST UPDATE HALF-STEP FOR VV METHODS############### */
 +        {
 +            if (ir->eI == eiVV && bInitStep)
 +            {
 +                /* if using velocity verlet with full time step Ekin,
 +                 * take the first half step only to compute the
 +                 * virial for the first step. From there,
 +                 * revert back to the initial coordinates
 +                 * so that the input is actually the initial step.
 +                 */
 +                copy_rvecn(state->v, cbuf, 0, state->natoms); /* should make this better for parallelizing? */
 +            }
 +            else
 +            {
 +                /* this is for NHC in the Ekin(t+dt/2) version of vv */
 +                trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ1);
 +            }
 +
 +            /* If we are using twin-range interactions where the long-range component
 +             * is only evaluated every nstcalclr>1 steps, we should do a special update
 +             * step to combine the long-range forces on these steps.
 +             * For nstcalclr=1 this is not done, since the forces would have been added
 +             * directly to the short-range forces already.
++             *
++             * TODO Remove various aspects of VV+twin-range in master
++             * branch, because VV integrators did not ever support
++             * twin-range multiple time stepping with constraints.
 +             */
 +            bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +            update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC,
 +                          f, bUpdateDoLR, fr->f_twin, fcd,
 +                          ekind, M, upd, bInitStep, etrtVELOCITY1,
 +                          cr, nrnb, constr, &top->idef);
 +
 +            if (bIterativeCase && do_per_step(step-1, ir->nstpcouple) && !bInitStep)
 +            {
 +                gmx_iterate_init(&iterate, TRUE);
 +            }
 +            /* for iterations, we save these vectors, as we will be self-consistently iterating
 +               the calculations */
 +
 +            /*#### UPDATE EXTENDED VARIABLES IN TROTTER FORMULATION */
 +
 +            /* save the state */
 +            if (iterate.bIterationActive)
 +            {
 +                copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts));
 +            }
 +
 +            bFirstIterate = TRUE;
 +            while (bFirstIterate || iterate.bIterationActive)
 +            {
 +                if (iterate.bIterationActive)
 +                {
 +                    copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts));
 +                    if (bFirstIterate && bTrotter)
 +                    {
 +                        /* The first time through, we need a decent first estimate
 +                           of veta(t+dt) to compute the constraints.  Do
 +                           this by computing the box volume part of the
 +                           trotter integration at this time. Nothing else
 +                           should be changed by this routine here.  If
 +                           !(first time), we start with the previous value
 +                           of veta.  */
 +
 +                        veta_save = state->veta;
 +                        trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ0);
 +                        vetanew     = state->veta;
 +                        state->veta = veta_save;
 +                    }
 +                }
 +
 +                bOK = TRUE;
 +                if (!bRerunMD || rerun_fr.bV || bForceUpdate)     /* Why is rerun_fr.bV here?  Unclear. */
 +                {
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, shake_vir,
 +                                       cr, nrnb, wcycle, upd, constr,
 +                                       TRUE, bCalcVir, vetanew);
 +
 +                    if (!bOK)
 +                    {
 +                        gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
 +                    }
 +
 +                }
 +                else if (graph)
 +                {
 +                    /* Need to unshift here if a do_force has been
 +                       called in the previous step */
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +
 +                /* if VV, compute the pressure and constraints */
 +                /* For VV2, we strictly only need this if using pressure
 +                 * control, but we really would like to have accurate pressures
 +                 * printed out.
 +                 * Think about ways around this in the future?
 +                 * For now, keep this choice in comments.
 +                 */
 +                /*bPres = (ir->eI==eiVV || IR_NPT_TROTTER(ir)); */
 +                /*bTemp = ((ir->eI==eiVV &&(!bInitStep)) || (ir->eI==eiVVAK && IR_NPT_TROTTER(ir)));*/
 +                bPres = TRUE;
 +                bTemp = ((ir->eI == eiVV && (!bInitStep)) || (ir->eI == eiVVAK));
 +                if (bCalcEner && ir->eI == eiVVAK)  /*MRS:  7/9/2010 -- this still doesn't fix it?*/
 +                {
 +                    bSumEkinhOld = TRUE;
 +                }
 +                /* for vv, the first half of the integration actually corresponds to the previous step.
 +                   So we need information from the last step in the first half of the integration */
 +                if (bGStat || do_per_step(step-1, nstglobalcomm))
 +                {
 +                    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                    wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                    constr, NULL, FALSE, state->box,
 +                                    top_global, &bSumEkinhOld,
 +                                    cglo_flags
 +                                    | CGLO_ENERGY
 +                                    | (bTemp ? CGLO_TEMPERATURE : 0)
 +                                    | (bPres ? CGLO_PRESSURE : 0)
 +                                    | (bPres ? CGLO_CONSTRAINT : 0)
 +                                    | ((iterate.bIterationActive) ? CGLO_ITERATE : 0)
 +                                    | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
 +                                    | CGLO_SCALEEKIN
 +                                    );
 +                    /* explanation of above:
 +                       a) We compute Ekin at the full time step
 +                       if 1) we are using the AveVel Ekin, and it's not the
 +                       initial step, or 2) if we are using AveEkin, but need the full
 +                       time step kinetic energy for the pressure (always true now, since we want accurate statistics).
 +                       b) If we are using EkinAveEkin for the kinetic energy for the temperature control, we still feed in
 +                       EkinAveVel because it's needed for the pressure */
 +                }
 +                /* temperature scaling and pressure scaling to produce the extended variables at t+dt */
 +                if (!bInitStep)
 +                {
 +                    if (bTrotter)
 +                    {
 +                        m_add(force_vir, shake_vir, total_vir); /* we need the un-dispersion corrected total vir here */
 +                        trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ2);
 +                    }
 +                    else
 +                    {
 +                        if (bExchanged)
 +                        {
 +
 +                            /* We need the kinetic energy at minus the half step for determining
 +                             * the full step kinetic energy and possibly for T-coupling.*/
 +                            /* This may not be quite working correctly yet . . . . */
 +                            compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                            wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot,
 +                                            constr, NULL, FALSE, state->box,
 +                                            top_global, &bSumEkinhOld,
 +                                            CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
 +                        }
 +                    }
 +                }
 +
 +                if (iterate.bIterationActive &&
 +                    done_iterating(cr, fplog, step, &iterate, bFirstIterate,
 +                                   state->veta, &vetanew))
 +                {
 +                    break;
 +                }
 +                bFirstIterate = FALSE;
 +            }
 +
 +            if (bTrotter && !bInitStep)
 +            {
 +                copy_mat(shake_vir, state->svir_prev);
 +                copy_mat(force_vir, state->fvir_prev);
 +                if (IR_NVT_TROTTER(ir) && ir->eI == eiVV)
 +                {
 +                    /* update temperature and kinetic energy now that step is over - this is the v(t+dt) point */
 +                    enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, NULL, (ir->eI == eiVV), FALSE);
 +                    enerd->term[F_EKIN] = trace(ekind->ekin);
 +                }
 +            }
 +            /* if it's the initial step, we performed this first step just to get the constraint virial */
 +            if (bInitStep && ir->eI == eiVV)
 +            {
 +                copy_rvecn(cbuf, state->v, 0, state->natoms);
 +            }
 +        }
 +
 +        /* MRS -- now done iterating -- compute the conserved quantity */
 +        if (bVV)
 +        {
 +            saved_conserved_quantity = compute_conserved_from_auxiliary(ir, state, &MassQ);
 +            if (ir->eI == eiVV)
 +            {
 +                last_ekin = enerd->term[F_EKIN];
 +            }
 +            if ((ir->eDispCorr != edispcEnerPres) && (ir->eDispCorr != edispcAllEnerPres))
 +            {
 +                saved_conserved_quantity -= enerd->term[F_DISPCORR];
 +            }
 +            /* sum up the foreign energy and dhdl terms for vv.  currently done every step so that dhdl is correct in the .edr */
 +            if (!bRerunMD)
 +            {
 +                sum_dhdl(enerd, state->lambda, ir->fepvals);
 +            }
 +        }
 +
 +        /* ########  END FIRST UPDATE STEP  ############## */
 +        /* ########  If doing VV, we now have v(dt) ###### */
 +        if (bDoExpanded)
 +        {
 +            /* perform extended ensemble sampling in lambda - we don't
 +               actually move to the new state before outputting
 +               statistics, but if performing simulated tempering, we
 +               do update the velocities and the tau_t. */
 +
 +            lamnew = ExpandedEnsembleDynamics(fplog, ir, enerd, state, &MassQ, state->fep_state, &state->dfhist, step, state->v, mdatoms);
 +            /* history is maintained in state->dfhist, but state_global is what is sent to trajectory and log output */
 +            copy_df_history(&state_global->dfhist, &state->dfhist);
 +        }
 +
 +        /* Now we have the energies and forces corresponding to the
 +         * coordinates at time t. We must output all of this before
 +         * the update.
 +         */
 +        do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t,
 +                                 ir, state, state_global, top_global, fr,
 +                                 outf, mdebin, ekind, f, f_global,
 +                                 wcycle, &nchkpt,
 +                                 bCPT, bRerunMD, bLastStep, (Flags & MD_CONFOUT),
 +                                 bSumEkinhOld);
 +        /* Check if IMD step and do IMD communication, if bIMD is TRUE. */
 +        bIMDstep = do_IMD(ir->bIMD, step, cr, bNS, state->box, state->x, ir, t, wcycle);
 +
 +        /* kludge -- virial is lost with restart for NPT control. Must restart */
 +        if (bStartingFromCpt && bVV)
 +        {
 +            copy_mat(state->svir_prev, shake_vir);
 +            copy_mat(state->fvir_prev, force_vir);
 +        }
 +
 +        elapsed_time = walltime_accounting_get_current_elapsed_time(walltime_accounting);
 +
 +        /* Check whether everything is still allright */
 +        if (((int)gmx_get_stop_condition() > handled_stop_condition)
 +#ifdef GMX_THREAD_MPI
 +            && MASTER(cr)
 +#endif
 +            )
 +        {
 +            /* this is just make gs.sig compatible with the hack
 +               of sending signals around by MPI_Reduce with together with
 +               other floats */
 +            if (gmx_get_stop_condition() == gmx_stop_cond_next_ns)
 +            {
 +                gs.sig[eglsSTOPCOND] = 1;
 +            }
 +            if (gmx_get_stop_condition() == gmx_stop_cond_next)
 +            {
 +                gs.sig[eglsSTOPCOND] = -1;
 +            }
 +            /* < 0 means stop at next step, > 0 means stop at next NS step */
 +            if (fplog)
 +            {
 +                fprintf(fplog,
 +                        "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
 +                        gmx_get_signal_name(),
 +                        gs.sig[eglsSTOPCOND] == 1 ? "NS " : "");
 +                fflush(fplog);
 +            }
 +            fprintf(stderr,
 +                    "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
 +                    gmx_get_signal_name(),
 +                    gs.sig[eglsSTOPCOND] == 1 ? "NS " : "");
 +            fflush(stderr);
 +            handled_stop_condition = (int)gmx_get_stop_condition();
 +        }
 +        else if (MASTER(cr) && (bNS || ir->nstlist <= 0) &&
 +                 (max_hours > 0 && elapsed_time > max_hours*60.0*60.0*0.99) &&
 +                 gs.sig[eglsSTOPCOND] == 0 && gs.set[eglsSTOPCOND] == 0)
 +        {
 +            /* Signal to terminate the run */
 +            gs.sig[eglsSTOPCOND] = 1;
 +            if (fplog)
 +            {
 +                fprintf(fplog, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99);
 +            }
 +            fprintf(stderr, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99);
 +        }
 +
 +        if (bResetCountersHalfMaxH && MASTER(cr) &&
 +            elapsed_time > max_hours*60.0*60.0*0.495)
 +        {
 +            gs.sig[eglsRESETCOUNTERS] = 1;
 +        }
 +
 +        if (ir->nstlist == -1 && !bRerunMD)
 +        {
 +            /* When bGStatEveryStep=FALSE, global_stat is only called
 +             * when we check the atom displacements, not at NS steps.
 +             * This means that also the bonded interaction count check is not
 +             * performed immediately after NS. Therefore a few MD steps could
 +             * be performed with missing interactions.
 +             * But wrong energies are never written to file,
 +             * since energies are only written after global_stat
 +             * has been called.
 +             */
 +            if (step >= nlh.step_nscheck)
 +            {
 +                nlh.nabnsb = natoms_beyond_ns_buffer(ir, fr, &top->cgs,
 +                                                     nlh.scale_tot, state->x);
 +            }
 +            else
 +            {
 +                /* This is not necessarily true,
 +                 * but step_nscheck is determined quite conservatively.
 +                 */
 +                nlh.nabnsb = 0;
 +            }
 +        }
 +
 +        /* In parallel we only have to check for checkpointing in steps
 +         * where we do global communication,
 +         *  otherwise the other nodes don't know.
 +         */
 +        if (MASTER(cr) && ((bGStat || !PAR(cr)) &&
 +                           cpt_period >= 0 &&
 +                           (cpt_period == 0 ||
 +                            elapsed_time >= nchkpt*cpt_period*60.0)) &&
 +            gs.set[eglsCHKPT] == 0)
 +        {
 +            gs.sig[eglsCHKPT] = 1;
 +        }
 +
 +        /* at the start of step, randomize or scale the velocities (trotter done elsewhere) */
 +        if (EI_VV(ir->eI))
 +        {
 +            if (!bInitStep)
 +            {
 +                update_tcouple(step, ir, state, ekind, &MassQ, mdatoms);
 +            }
 +            if (ETC_ANDERSEN(ir->etc)) /* keep this outside of update_tcouple because of the extra info required to pass */
 +            {
 +                gmx_bool bIfRandomize;
 +                bIfRandomize = update_randomize_velocities(ir, step, cr, mdatoms, state, upd, constr);
 +                /* if we have constraints, we have to remove the kinetic energy parallel to the bonds */
 +                if (constr && bIfRandomize)
 +                {
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, tmp_vir,
 +                                       cr, nrnb, wcycle, upd, constr,
 +                                       TRUE, bCalcVir, vetanew);
 +                }
 +            }
 +        }
 +
 +        if (bIterativeCase && do_per_step(step, ir->nstpcouple))
 +        {
 +            gmx_iterate_init(&iterate, TRUE);
 +            /* for iterations, we save these vectors, as we will be redoing the calculations */
 +            copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts));
 +        }
 +
 +        bFirstIterate = TRUE;
 +        while (bFirstIterate || iterate.bIterationActive)
 +        {
 +            /* We now restore these vectors to redo the calculation with improved extended variables */
 +            if (iterate.bIterationActive)
 +            {
 +                copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts));
 +            }
 +
 +            /* We make the decision to break or not -after- the calculation of Ekin and Pressure,
 +               so scroll down for that logic */
 +
 +            /* #########   START SECOND UPDATE STEP ################# */
 +            /* Box is changed in update() when we do pressure coupling,
 +             * but we should still use the old box for energy corrections and when
 +             * writing it to the energy file, so it matches the trajectory files for
 +             * the same timestep above. Make a copy in a separate array.
 +             */
 +            copy_mat(state->box, lastbox);
 +
 +            bOK         = TRUE;
 +            dvdl_constr = 0;
 +
 +            if (!(bRerunMD && !rerun_fr.bV && !bForceUpdate))
 +            {
 +                wallcycle_start(wcycle, ewcUPDATE);
 +                /* UPDATE PRESSURE VARIABLES IN TROTTER FORMULATION WITH CONSTRAINTS */
 +                if (bTrotter)
 +                {
 +                    if (iterate.bIterationActive)
 +                    {
 +                        if (bFirstIterate)
 +                        {
 +                            scalevir = 1;
 +                        }
 +                        else
 +                        {
 +                            /* we use a new value of scalevir to converge the iterations faster */
 +                            scalevir = tracevir/trace(shake_vir);
 +                        }
 +                        msmul(shake_vir, scalevir, shake_vir);
 +                        m_add(force_vir, shake_vir, total_vir);
 +                        clear_mat(shake_vir);
 +                    }
 +                    trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ3);
 +                    /* We can only do Berendsen coupling after we have summed
 +                     * the kinetic energy or virial. Since the happens
 +                     * in global_state after update, we should only do it at
 +                     * step % nstlist = 1 with bGStatEveryStep=FALSE.
 +                     */
 +                }
 +                else
 +                {
 +                    update_tcouple(step, ir, state, ekind, &MassQ, mdatoms);
 +                    update_pcouple(fplog, step, ir, state, pcoupl_mu, M, bInitStep);
 +                }
 +
 +                if (bVV)
 +                {
 +                    bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                    /* velocity half-step update */
 +                    update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                                  bUpdateDoLR, fr->f_twin, fcd,
 +                                  ekind, M, upd, FALSE, etrtVELOCITY2,
 +                                  cr, nrnb, constr, &top->idef);
 +                }
 +
 +                /* Above, initialize just copies ekinh into ekin,
 +                 * it doesn't copy position (for VV),
 +                 * and entire integrator for MD.
 +                 */
 +
 +                if (ir->eI == eiVVAK)
 +                {
 +                    copy_rvecn(state->x, cbuf, 0, state->natoms);
 +                }
 +                bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                              bUpdateDoLR, fr->f_twin, fcd,
 +                              ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef);
 +                wallcycle_stop(wcycle, ewcUPDATE);
 +
 +                update_constraints(fplog, step, &dvdl_constr, ir, ekind, mdatoms, state,
 +                                   fr->bMolPBC, graph, f,
 +                                   &top->idef, shake_vir,
 +                                   cr, nrnb, wcycle, upd, constr,
 +                                   FALSE, bCalcVir, state->veta);
 +
 +                if (ir->eI == eiVVAK)
 +                {
 +                    /* erase F_EKIN and F_TEMP here? */
 +                    /* just compute the kinetic energy at the half step to perform a trotter step */
 +                    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                    wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                    constr, NULL, FALSE, lastbox,
 +                                    top_global, &bSumEkinhOld,
 +                                    cglo_flags | CGLO_TEMPERATURE
 +                                    );
 +                    wallcycle_start(wcycle, ewcUPDATE);
 +                    trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ4);
 +                    /* now we know the scaling, we can compute the positions again again */
 +                    copy_rvecn(cbuf, state->x, 0, state->natoms);
 +
 +                    bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                    update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                                  bUpdateDoLR, fr->f_twin, fcd,
 +                                  ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef);
 +                    wallcycle_stop(wcycle, ewcUPDATE);
 +
 +                    /* do we need an extra constraint here? just need to copy out of state->v to upd->xp? */
 +                    /* are the small terms in the shake_vir here due
 +                     * to numerical errors, or are they important
 +                     * physically? I'm thinking they are just errors, but not completely sure.
 +                     * For now, will call without actually constraining, constr=NULL*/
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, tmp_vir,
 +                                       cr, nrnb, wcycle, upd, NULL,
 +                                       FALSE, bCalcVir,
 +                                       state->veta);
 +                }
 +                if (!bOK)
 +                {
 +                    gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
 +                }
 +
 +                if (fr->bSepDVDL && fplog && do_log)
 +                {
 +                    gmx_print_sepdvdl(fplog, "Constraint dV/dl", 0.0, dvdl_constr);
 +                }
 +                if (bVV)
 +                {
 +                    /* this factor or 2 correction is necessary
 +                       because half of the constraint force is removed
 +                       in the vv step, so we have to double it.  See
 +                       the Redmine issue #1255.  It is not yet clear
 +                       if the factor of 2 is exact, or just a very
 +                       good approximation, and this will be
 +                       investigated.  The next step is to see if this
 +                       can be done adding a dhdl contribution from the
 +                       rattle step, but this is somewhat more
 +                       complicated with the current code. Will be
 +                       investigated, hopefully for 4.6.3. However,
 +                       this current solution is much better than
 +                       having it completely wrong.
 +                     */
 +                    enerd->term[F_DVDL_CONSTR] += 2*dvdl_constr;
 +                }
 +                else
 +                {
 +                    enerd->term[F_DVDL_CONSTR] += dvdl_constr;
 +                }
 +            }
 +            else if (graph)
 +            {
 +                /* Need to unshift here */
 +                unshift_self(graph, state->box, state->x);
 +            }
 +
 +            if (vsite != NULL)
 +            {
 +                wallcycle_start(wcycle, ewcVSITECONSTR);
 +                if (graph != NULL)
 +                {
 +                    shift_self(graph, state->box, state->x);
 +                }
 +                construct_vsites(vsite, state->x, ir->delta_t, state->v,
 +                                 top->idef.iparams, top->idef.il,
 +                                 fr->ePBC, fr->bMolPBC, cr, state->box);
 +
 +                if (graph != NULL)
 +                {
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +                wallcycle_stop(wcycle, ewcVSITECONSTR);
 +            }
 +
 +            /* ############## IF NOT VV, Calculate globals HERE, also iterate constraints  ############ */
 +            /* With Leap-Frog we can skip compute_globals at
 +             * non-communication steps, but we need to calculate
 +             * the kinetic energy one step before communication.
 +             */
 +            if (bGStat || (!EI_VV(ir->eI) && do_per_step(step+1, nstglobalcomm)))
 +            {
 +                if (ir->nstlist == -1 && bFirstIterate)
 +                {
 +                    gs.sig[eglsNABNSB] = nlh.nabnsb;
 +                }
 +                compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                constr,
 +                                bFirstIterate ? &gs : NULL,
 +                                (step_rel % gs.nstms == 0) &&
 +                                (multisim_nsteps < 0 || (step_rel < multisim_nsteps)),
 +                                lastbox,
 +                                top_global, &bSumEkinhOld,
 +                                cglo_flags
 +                                | (!EI_VV(ir->eI) || bRerunMD ? CGLO_ENERGY : 0)
 +                                | (!EI_VV(ir->eI) && bStopCM ? CGLO_STOPCM : 0)
 +                                | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0)
 +                                | (!EI_VV(ir->eI) || bRerunMD ? CGLO_PRESSURE : 0)
 +                                | (iterate.bIterationActive ? CGLO_ITERATE : 0)
 +                                | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
 +                                | CGLO_CONSTRAINT
 +                                );
 +                if (ir->nstlist == -1 && bFirstIterate)
 +                {
 +                    nlh.nabnsb         = gs.set[eglsNABNSB];
 +                    gs.set[eglsNABNSB] = 0;
 +                }
 +            }
 +            /* bIterate is set to keep it from eliminating the old ekin kinetic energy terms */
 +            /* #############  END CALC EKIN AND PRESSURE ################# */
 +
 +            /* Note: this is OK, but there are some numerical precision issues with using the convergence of
 +               the virial that should probably be addressed eventually. state->veta has better properies,
 +               but what we actually need entering the new cycle is the new shake_vir value. Ideally, we could
 +               generate the new shake_vir, but test the veta value for convergence.  This will take some thought. */
 +
 +            if (iterate.bIterationActive &&
 +                done_iterating(cr, fplog, step, &iterate, bFirstIterate,
 +                               trace(shake_vir), &tracevir))
 +            {
 +                break;
 +            }
 +            bFirstIterate = FALSE;
 +        }
 +
 +        if (!bVV || bRerunMD)
 +        {
 +            /* sum up the foreign energy and dhdl terms for md and sd. currently done every step so that dhdl is correct in the .edr */
 +            sum_dhdl(enerd, state->lambda, ir->fepvals);
 +        }
 +        update_box(fplog, step, ir, mdatoms, state, f,
 +                   ir->nstlist == -1 ? &nlh.scale_tot : NULL, pcoupl_mu, nrnb, upd);
 +
 +        /* ################# END UPDATE STEP 2 ################# */
 +        /* #### We now have r(t+dt) and v(t+dt/2)  ############# */
 +
 +        /* The coordinates (x) were unshifted in update */
 +        if (!bGStat)
 +        {
 +            /* We will not sum ekinh_old,
 +             * so signal that we still have to do it.
 +             */
 +            bSumEkinhOld = TRUE;
 +        }
 +
 +        /* #########  BEGIN PREPARING EDR OUTPUT  ###########  */
 +
 +        /* use the directly determined last velocity, not actually the averaged half steps */
 +        if (bTrotter && ir->eI == eiVV)
 +        {
 +            enerd->term[F_EKIN] = last_ekin;
 +        }
 +        enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
 +
 +        if (bVV)
 +        {
 +            enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + saved_conserved_quantity;
 +        }
 +        else
 +        {
 +            enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + compute_conserved_from_auxiliary(ir, state, &MassQ);
 +        }
 +        /* #########  END PREPARING EDR OUTPUT  ###########  */
 +
 +        /* Output stuff */
 +        if (MASTER(cr))
 +        {
 +            gmx_bool do_dr, do_or;
 +
 +            if (fplog && do_log && bDoExpanded)
 +            {
 +                /* only needed if doing expanded ensemble */
 +                PrintFreeEnergyInfoToFile(fplog, ir->fepvals, ir->expandedvals, ir->bSimTemp ? ir->simtempvals : NULL,
 +                                          &state_global->dfhist, state->fep_state, ir->nstlog, step);
 +            }
 +            if (!(bStartingFromCpt && (EI_VV(ir->eI))))
 +            {
 +                if (bCalcEner)
 +                {
 +                    upd_mdebin(mdebin, bDoDHDL, TRUE,
 +                               t, mdatoms->tmass, enerd, state,
 +                               ir->fepvals, ir->expandedvals, lastbox,
 +                               shake_vir, force_vir, total_vir, pres,
 +                               ekind, mu_tot, constr);
 +                }
 +                else
 +                {
 +                    upd_mdebin_step(mdebin);
 +                }
 +
 +                do_dr  = do_per_step(step, ir->nstdisreout);
 +                do_or  = do_per_step(step, ir->nstorireout);
 +
 +                print_ebin(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or, do_log ? fplog : NULL,
 +                           step, t,
 +                           eprNORMAL, bCompact, mdebin, fcd, groups, &(ir->opts));
 +            }
 +            if (ir->ePull != epullNO)
 +            {
 +                pull_print_output(ir->pull, step, t);
 +            }
 +
 +            if (do_per_step(step, ir->nstlog))
 +            {
 +                if (fflush(fplog) != 0)
 +                {
 +                    gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?");
 +                }
 +            }
 +        }
 +        if (bDoExpanded)
 +        {
 +            /* Have to do this part _after_ outputting the logfile and the edr file */
 +            /* Gets written into the state at the beginning of next loop*/
 +            state->fep_state = lamnew;
 +        }
 +        /* Print the remaining wall clock time for the run */
 +        if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal()) && !bPMETuneRunning)
 +        {
 +            if (shellfc)
 +            {
 +                fprintf(stderr, "\n");
 +            }
 +            print_time(stderr, walltime_accounting, step, ir, cr);
 +        }
 +
 +        /* Ion/water position swapping.
 +         * Not done in last step since trajectory writing happens before this call
 +         * in the MD loop and exchanges would be lost anyway. */
 +        bNeedRepartition = FALSE;
 +        if ((ir->eSwapCoords != eswapNO) && (step > 0) && !bLastStep &&
 +            do_per_step(step, ir->swap->nstswap))
 +        {
 +            bNeedRepartition = do_swapcoords(cr, step, t, ir, wcycle,
 +                                             bRerunMD ? rerun_fr.x   : state->x,
 +                                             bRerunMD ? rerun_fr.box : state->box,
 +                                             top_global, MASTER(cr) && bVerbose, bRerunMD);
 +
 +            if (bNeedRepartition && DOMAINDECOMP(cr))
 +            {
 +                dd_collect_state(cr->dd, state, state_global);
 +            }
 +        }
 +
 +        /* Replica exchange */
 +        bExchanged = FALSE;
 +        if ((repl_ex_nst > 0) && (step > 0) && !bLastStep &&
 +            do_per_step(step, repl_ex_nst))
 +        {
 +            bExchanged = replica_exchange(fplog, cr, repl_ex,
 +                                          state_global, enerd,
 +                                          state, step, t);
 +        }
 +
 +        if ( (bExchanged || bNeedRepartition) && DOMAINDECOMP(cr) )
 +        {
 +            dd_partition_system(fplog, step, cr, TRUE, 1,
 +                                state_global, top_global, ir,
 +                                state, &f, mdatoms, top, fr,
 +                                vsite, shellfc, constr,
 +                                nrnb, wcycle, FALSE);
 +        }
 +
 +        bFirstStep       = FALSE;
 +        bInitStep        = FALSE;
 +        bStartingFromCpt = FALSE;
 +
 +        /* #######  SET VARIABLES FOR NEXT ITERATION IF THEY STILL NEED IT ###### */
 +        /* With all integrators, except VV, we need to retain the pressure
 +         * at the current step for coupling at the next step.
 +         */
 +        if ((state->flags & (1<<estPRES_PREV)) &&
 +            (bGStatEveryStep ||
 +             (ir->nstpcouple > 0 && step % ir->nstpcouple == 0)))
 +        {
 +            /* Store the pressure in t_state for pressure coupling
 +             * at the next MD step.
 +             */
 +            copy_mat(pres, state->pres_prev);
 +        }
 +
 +        /* #######  END SET VARIABLES FOR NEXT ITERATION ###### */
 +
 +        if ( (membed != NULL) && (!bLastStep) )
 +        {
 +            rescale_membed(step_rel, membed, state_global->x);
 +        }
 +
 +        if (bRerunMD)
 +        {
 +            if (MASTER(cr))
 +            {
 +                /* read next frame from input trajectory */
 +                bNotLastFrame = read_next_frame(oenv, status, &rerun_fr);
 +            }
 +
 +            if (PAR(cr))
 +            {
 +                rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame);
 +            }
 +        }
 +
 +        if (!bRerunMD || !rerun_fr.bStep)
 +        {
 +            /* increase the MD step number */
 +            step++;
 +            step_rel++;
 +        }
 +
 +        cycles = wallcycle_stop(wcycle, ewcSTEP);
 +        if (DOMAINDECOMP(cr) && wcycle)
 +        {
 +            dd_cycles_add(cr->dd, cycles, ddCyclStep);
 +        }
 +
 +        if (bPMETuneRunning || bPMETuneTry)
 +        {
 +            /* PME grid + cut-off optimization with GPUs or PME nodes */
 +
 +            /* Count the total cycles over the last steps */
 +            cycles_pmes += cycles;
 +
 +            /* We can only switch cut-off at NS steps */
 +            if (step % ir->nstlist == 0)
 +            {
 +                /* PME grid + cut-off optimization with GPUs or PME nodes */
 +                if (bPMETuneTry)
 +                {
 +                    if (DDMASTER(cr->dd))
 +                    {
 +                        /* PME node load is too high, start tuning */
 +                        bPMETuneRunning = (dd_pme_f_ratio(cr->dd) >= 1.05);
 +                    }
 +                    dd_bcast(cr->dd, sizeof(gmx_bool), &bPMETuneRunning);
 +
 +                    if (bPMETuneRunning || step_rel > ir->nstlist*50)
 +                    {
 +                        bPMETuneTry     = FALSE;
 +                    }
 +                }
 +                if (bPMETuneRunning)
 +                {
 +                    /* init_step might not be a multiple of nstlist,
 +                     * but the first cycle is always skipped anyhow.
 +                     */
 +                    bPMETuneRunning =
 +                        pme_load_balance(pme_loadbal, cr,
 +                                         (bVerbose && MASTER(cr)) ? stderr : NULL,
 +                                         fplog,
 +                                         ir, state, cycles_pmes,
 +                                         fr->ic, fr->nbv, &fr->pmedata,
 +                                         step);
 +
 +                    /* Update constants in forcerec/inputrec to keep them in sync with fr->ic */
 +                    fr->ewaldcoeff_q  = fr->ic->ewaldcoeff_q;
 +                    fr->ewaldcoeff_lj = fr->ic->ewaldcoeff_lj;
 +                    fr->rlist         = fr->ic->rlist;
 +                    fr->rlistlong     = fr->ic->rlistlong;
 +                    fr->rcoulomb      = fr->ic->rcoulomb;
 +                    fr->rvdw          = fr->ic->rvdw;
 +
 +                    if (ir->eDispCorr != edispcNO)
 +                    {
 +                        calc_enervirdiff(NULL, ir->eDispCorr, fr);
 +                    }
 +                }
 +                cycles_pmes = 0;
 +            }
 +        }
 +
 +        if (step_rel == wcycle_get_reset_counters(wcycle) ||
 +            gs.set[eglsRESETCOUNTERS] != 0)
 +        {
 +            /* Reset all the counters related to performance over the run */
 +            reset_all_counters(fplog, cr, step, &step_rel, ir, wcycle, nrnb, walltime_accounting,
 +                               fr->nbv != NULL && fr->nbv->bUseGPU ? fr->nbv->cu_nbv : NULL);
 +            wcycle_set_reset_counters(wcycle, -1);
 +            if (!(cr->duty & DUTY_PME))
 +            {
 +                /* Tell our PME node to reset its counters */
 +                gmx_pme_send_resetcounters(cr, step);
 +            }
 +            /* Correct max_hours for the elapsed time */
 +            max_hours                -= elapsed_time/(60.0*60.0);
 +            bResetCountersHalfMaxH    = FALSE;
 +            gs.set[eglsRESETCOUNTERS] = 0;
 +        }
 +
 +        /* If bIMD is TRUE, the master updates the IMD energy record and sends positions to VMD client */
 +        IMD_prep_energies_send_positions(ir->bIMD && MASTER(cr), bIMDstep, ir->imd, enerd, step, bCalcEner, wcycle);
 +
 +    }
 +    /* End of main MD loop */
 +    debug_gmx();
 +
 +    /* Stop measuring walltime */
 +    walltime_accounting_end(walltime_accounting);
 +
 +    if (bRerunMD && MASTER(cr))
 +    {
 +        close_trj(status);
 +    }
 +
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Tell the PME only node to finish */
 +        gmx_pme_send_finish(cr);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        if (ir->nstcalcenergy > 0 && !bRerunMD)
 +        {
 +            print_ebin(mdoutf_get_fp_ene(outf), FALSE, FALSE, FALSE, fplog, step, t,
 +                       eprAVER, FALSE, mdebin, fcd, groups, &(ir->opts));
 +        }
 +    }
 +
 +    done_mdoutf(outf);
 +    debug_gmx();
 +
 +    if (ir->nstlist == -1 && nlh.nns > 0 && fplog)
 +    {
 +        fprintf(fplog, "Average neighborlist lifetime: %.1f steps, std.dev.: %.1f steps\n", nlh.s1/nlh.nns, sqrt(nlh.s2/nlh.nns - sqr(nlh.s1/nlh.nns)));
 +        fprintf(fplog, "Average number of atoms that crossed the half buffer length: %.1f\n\n", nlh.ab/nlh.nns);
 +    }
 +
 +    if (pme_loadbal != NULL)
 +    {
 +        pme_loadbal_done(pme_loadbal, cr, fplog,
 +                         fr->nbv != NULL && fr->nbv->bUseGPU);
 +    }
 +
 +    if (shellfc && fplog)
 +    {
 +        fprintf(fplog, "Fraction of iterations that converged:           %.2f %%\n",
 +                (nconverged*100.0)/step_rel);
 +        fprintf(fplog, "Average number of force evaluations per MD step: %.2f\n\n",
 +                tcount/step_rel);
 +    }
 +
 +    if (repl_ex_nst > 0 && MASTER(cr))
 +    {
 +        print_replica_exchange_statistics(fplog, repl_ex);
 +    }
 +
 +    /* IMD cleanup, if bIMD is TRUE. */
 +    IMD_finalize(ir->bIMD, ir->imd);
 +
 +    walltime_accounting_set_nsteps_done(walltime_accounting, step_rel);
 +
 +    return 0;
 +}