Merge branch release-5-1
authorMark Abraham <mark.j.abraham@gmail.com>
Thu, 14 Apr 2016 09:23:52 +0000 (11:23 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Thu, 14 Apr 2016 10:04:52 +0000 (12:04 +0200)
Several changes from release-5-1 have been fixed separately
in master, such changes are omitted here.

Change-Id: Ib03a2091ad6f76e785f7a6cc9ea91493d246da3b

19 files changed:
1  2 
cmake/gmxManageGPU.cmake
docs/CMakeLists.txt
docs/manual/forcefield.tex
src/gromacs/correlationfunctions/autocorr.cpp
src/gromacs/correlationfunctions/manyautocorrelation.cpp
src/gromacs/correlationfunctions/tests/autocorr.cpp
src/gromacs/ewald/pme-load-balancing.cpp
src/gromacs/gmxana/gmx_dielectric.cpp
src/gromacs/gmxpreprocess/readir.cpp
src/gromacs/gmxpreprocess/toppush.cpp
src/gromacs/mdlib/constr.cpp
src/gromacs/mdlib/coupling.cpp
src/gromacs/mdlib/nbnxn_kernels/simd_2xnn/nbnxn_kernel_simd_2xnn.cpp
src/gromacs/mdlib/nbnxn_kernels/simd_4xn/nbnxn_kernel_simd_4xn.cpp
src/gromacs/mdlib/sim_util.cpp
src/gromacs/mdlib/wall.cpp
src/gromacs/simd/impl_ibm_vmx/impl_ibm_vmx.h
src/gromacs/utility/datafilefinder.cpp
src/programs/mdrun/runner.cpp

diff --combined cmake/gmxManageGPU.cmake
index 9b4f328ce71be9b7e6fd847d637ca8560f6ea570,fe1aa0ce9ba7841621f08e50821f5c1023a2a7c8..4e43c5e2d73f282d28905cdd4f449ed3399beea4
@@@ -76,6 -76,14 +76,6 @@@ if(GMX_GPU OR GMX_GPU_AUTO AND CAN_RUN_
      endif()
      find_package(CUDA ${REQUIRED_CUDA_VERSION} ${FIND_CUDA_QUIETLY})
  
 -    # The IBM xlc compiler chokes if we use both altivec and Cuda. Solve
 -    # this by not propagating the flags in this case, but add -O3
 -    # to make sure we don't turn off optimization.
 -    if(CMAKE_CXX_COMPILER_ID MATCHES "XL")
 -        set(CUDA_PROPAGATE_HOST_FLAGS OFF)
 -        list(APPEND CUDA_NVCC_FLAGS "-O3")
 -    endif()
 -
      # Cmake 2.8.12 (and CMake 3.0) introduced a new bug where the cuda
      # library dir is added twice as an rpath on APPLE, which in turn causes
      # the install_name_tool to wreck the binaries when it tries to remove this
@@@ -137,7 -145,7 +137,7 @@@ ${_msg}"
      if (NOT CUDA_FOUND)
          if (GMX_GPU_AUTO)
              # Disable GPU acceleration in auto mode
 -            message(STATUS "No compatible CUDA toolkit found (v4.0+), disabling native GPU acceleration")
 +            message(STATUS "No compatible CUDA toolkit found (v5.0+), disabling native GPU acceleration")
              set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
              set(CUDA_NOTFOUND_AUTO ON)
          else()
@@@ -175,7 -183,11 +175,11 @@@ endif(
  # We need to mark these advanced outside the conditional, otherwise, if the
  # user turns GMX_GPU=OFF after a failed cmake pass, these variables will be
  # left behind in the cache.
- mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_SDK_ROOT_DIR CUDA_VERBOSE_BUILD)
+ mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_SDK_ROOT_DIR CUDA_VERBOSE_BUILD # cmake 2.8.9 still spews these, check again when requirements change
+                  CUDA_SEPARABLE_COMPILATION      # not present at least with cmake 3.2, remove when required
+                  CUDA_USE_STATIC_CUDA_RUNTIME    # since cmake 3.3
+                  CUDA_dl_LIBRARY CUDA_rt_LIBRARY # - || -
+                  )
  if(NOT GMX_GPU)
      mark_as_advanced(CUDA_TOOLKIT_ROOT_DIR)
  endif()
@@@ -217,68 -229,52 +221,68 @@@ macro(get_cuda_compiler_info COMPILER_I
      endif()
  endmacro ()
  
 +include(CMakeDependentOption)
 +include(gmxOptionUtilities)
  macro(gmx_gpu_setup)
 -    # set up nvcc options
 -    include(gmxManageNvccConfig)
 +    if(GMX_GPU)
 +        # set up nvcc options
 +        include(gmxManageNvccConfig)
  
 -    gmx_check_if_changed(_cuda_version_changed CUDA_VERSION)
 +        gmx_check_if_changed(_cuda_version_changed CUDA_VERSION)
  
 -    # Generate CUDA RT API version string which will end up in config.h
 -    # We do this because nvcc is silly enough to not define its own version
 -    # (which should match the CUDA runtime API version AFAICT) and we want to
 -    # avoid creating the fragile dependency on cuda_runtime_api.h.
 -    #
 -    # NOTE: CUDA v7.5 is expected to have nvcc define it own version, so in the
 -    # future we should switch to using that version string instead of our own.
 -    if (NOT GMX_CUDA_VERSION OR _cuda_version_changed)
 -        MATH(EXPR GMX_CUDA_VERSION "${CUDA_VERSION_MAJOR}*1000 + ${CUDA_VERSION_MINOR}*10")
 -    endif()
 +        # Generate CUDA RT API version string which will end up in config.h
 +        # We do this because nvcc is silly enough to not define its own version
 +        # (which should match the CUDA runtime API version AFAICT) and we want to
 +        # avoid creating the fragile dependency on cuda_runtime_api.h.
 +        #
 +        # NOTE: CUDA v7.5 is expected to have nvcc define it own version, so in the
 +        # future we should switch to using that version string instead of our own.
 +        if (NOT GMX_CUDA_VERSION OR _cuda_version_changed)
 +            MATH(EXPR GMX_CUDA_VERSION "${CUDA_VERSION_MAJOR}*1000 + ${CUDA_VERSION_MINOR}*10")
 +        endif()
  
 -    if (_cuda_version_changed)
 -        # check the generated CUDA API version against the one present in cuda_runtime_api.h
 -        try_compile(_get_cuda_version_compile_res
 -            ${CMAKE_BINARY_DIR}
 -            ${CMAKE_SOURCE_DIR}/cmake/TestCUDAVersion.c
 -            COMPILE_DEFINITIONS "-DGMX_CUDA_VERSION=${GMX_CUDA_VERSION}"
 -            CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CUDA_TOOLKIT_INCLUDE}"
 -            OUTPUT_VARIABLE _get_cuda_version_compile_out)
 +        if (_cuda_version_changed)
 +            # check the generated CUDA API version against the one present in cuda_runtime_api.h
 +            try_compile(_get_cuda_version_compile_res
 +                ${CMAKE_BINARY_DIR}
 +                ${CMAKE_SOURCE_DIR}/cmake/TestCUDAVersion.c
 +                COMPILE_DEFINITIONS "-DGMX_CUDA_VERSION=${GMX_CUDA_VERSION}"
 +                CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CUDA_TOOLKIT_INCLUDE}"
 +                OUTPUT_VARIABLE _get_cuda_version_compile_out)
  
 -        if (NOT _get_cuda_version_compile_res)
 -            if (_get_cuda_version_compile_out MATCHES "CUDA version mismatch")
 -                message(FATAL_ERROR "The CUDA API version generated internally from the compiler version does not match the version reported by cuda.h. This means either that the CUDA detection picked up mismatching nvcc and the CUDA headers (likely not part of the same toolkit installation) or that there is an error in the internal version generation. If you are sure that it is not the former causing the error (check the relevant cache variables), define the GMX_CUDA_VERSION cache variable to work around the error.")
 -            else()
 -                message(FATAL_ERROR "Could not detect CUDA runtime API version")
 +            if (NOT _get_cuda_version_compile_res)
 +                if (_get_cuda_version_compile_out MATCHES "CUDA version mismatch")
 +                    message(FATAL_ERROR "The CUDA API version generated internally from the compiler version does not match the version reported by cuda.h. This means either that the CUDA detection picked up mismatching nvcc and the CUDA headers (likely not part of the same toolkit installation) or that there is an error in the internal version generation. If you are sure that it is not the former causing the error (check the relevant cache variables), define the GMX_CUDA_VERSION cache variable to work around the error.")
 +                else()
 +                    message(FATAL_ERROR "Could not detect CUDA runtime API version")
 +                endif()
              endif()
          endif()
 -    endif()
  
 -    # texture objects are supported in CUDA 5.0 and later
 -    if (CUDA_VERSION VERSION_GREATER 4.999)
 -        set(HAVE_CUDA_TEXOBJ_SUPPORT 1)
 -    endif()
 +        # no OpenMP is no good!
 +        if(NOT GMX_OPENMP)
 +            message(WARNING "To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading. Without OpenMP a single CPU core can be used with a GPU which is not optimal. Note that with MPI multiple processes can be forced to use a single GPU, but this is typically inefficient. You need to set both C and C++ compilers that support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
 +        endif()
 +    endif() # GMX_GPU
  
 -    # Atomic operations used for polling wait for GPU
 -    # (to avoid the cudaStreamSynchronize + ECC bug).
 -    # ThreadMPI is now always included. Thus, we don't check for Atomics anymore here.
 +    cmake_dependent_option(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT
 +        "Whether to compile the CUDA non-bonded module using a single compilation unit." ON
 +        "GMX_GPU" ON)
 +    mark_as_advanced(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT)
  
 -    # no OpenMP is no good!
 -    if(NOT GMX_OPENMP)
 -        message(WARNING "To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading. Without OpenMP a single CPU core can be used with a GPU which is not optimal. Note that with MPI multiple processes can be forced to use a single GPU, but this is typically inefficient. You need to set both C and C++ compilers that support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
 +    if (GMX_GPU)
 +        # We need to use single compilation unit for kernels:
 +        # - when compiling for CC 2.x devices where buggy kernel code is generated
 +        gmx_check_if_changed(_gmx_cuda_target_changed GMX_CUDA_TARGET_SM GMX_CUDA_TARGET_COMPUTE CUDA_NVCC_FLAGS)
 +        if(_gmx_cuda_target_changed OR NOT GMX_GPU_DETECTION_DONE)
 +            if((NOT GMX_CUDA_TARGET_SM AND NOT GMX_CUDA_TARGET_COMPUTE) OR
 +               (GMX_CUDA_TARGET_SM MATCHES "2[01]" OR GMX_CUDA_TARGET_COMPUTE MATCHES "2[01]"))
 +               message(STATUS "Enabling single compilation unit for the CUDA non-bonded module. Multiple compilation units are not compatible with CC 2.x devices, to enable the feature specify only CC >=3.0 target architectures in GMX_CUDA_TARGET_SM/GMX_CUDA_TARGET_COMPUTE.")
 +                set_property(CACHE GMX_CUDA_NB_SINGLE_COMPILATION_UNIT PROPERTY VALUE ON)
 +            else()
 +                message(STATUS "Enabling multiple compilation units for the CUDA non-bonded module.")
 +                set_property(CACHE GMX_CUDA_NB_SINGLE_COMPILATION_UNIT PROPERTY VALUE OFF)
 +            endif()
 +        endif()
      endif()
  endmacro()
diff --combined docs/CMakeLists.txt
index a7d08fa03659395db46aa52aa7c5829e1af0441c,134255599c0a31af82e1756f8c500b1e1403e205..7ad94c44b518f27d6c1dc594955cc3453ff2c00e
@@@ -58,7 -58,7 +58,7 @@@ mark_as_advanced(SOURCE_MD5SUM
  
  set(EXPECTED_DOXYGEN_VERSION 1.8.5)
  
 -find_package(PythonInterp)
 +find_package(PythonInterp 2.7)
  find_package(Sphinx 1.2.3 QUIET COMPONENTS pygments)
  
  # Even if we aren't going to make the full webpage, set up to put all
@@@ -86,14 -86,14 +86,14 @@@ if (SPHINX_FOUND
      set(SPHINX_CONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/conf.py)
      set(SPHINX_CONFIG_VARS_FILE ${SPHINX_INPUT_DIR}/conf-vars.py)
      set(SPHINX_EXTENSION_PATH ${CMAKE_CURRENT_SOURCE_DIR})
 -    if (SOURCE_IS_SOURCE_DISTRIBUTION OR GMX_BUILD_TARBALL)
 -        # The real build of the webpage happens from the tarball, and
 -        # this should be set to the matching MD5 sum.
 -        set(REGRESSIONTEST_MD5SUM_STRING "${REGRESSIONTEST_MD5SUM}")
 -    else()
 +    if (SOURCE_MD5SUM STREQUAL "unknown")
          # But for testing the webpage build (e.g. from the repo) we
          # need a default value.
          set(REGRESSIONTEST_MD5SUM_STRING "unknown")
 +    else()
 +        # The real build of the webpage happens from the tarball, and
 +        # this should be set to the matching MD5 sum.
 +        set(REGRESSIONTEST_MD5SUM_STRING "${REGRESSIONTEST_MD5SUM}")
      endif()
      set(SPHINX_SOURCE_FILES
          index.rst
          dev-manual/gmxtree.rst
          dev-manual/includestyle.rst
          dev-manual/jenkins.rst
 +        dev-manual/language-features.rst
          dev-manual/naming.rst
          dev-manual/overview.rst
          dev-manual/relocatable-binaries.rst
@@@ -269,10 -268,6 +269,6 @@@ if (MAN_PAGE_DIR
          DESTINATION ${MAN_INSTALL_DIR}/man1
          COMPONENT man OPTIONAL
          FILES_MATCHING PATTERN "*.1")
-     install(DIRECTORY ${MAN_PAGE_DIR}/
-         DESTINATION ${MAN_INSTALL_DIR}/man7
-         COMPONENT man OPTIONAL
-         FILES_MATCHING PATTERN "*.7")
  endif()
  gmx_cpack_add_generated_source_directory(man)
  
index 169c13d4f94572fc785e1ce0424ea67ade1a2f5c,4c32b09fd415bbc919d362686733960d77d0e430..063849ef2cf35789b480146a13ad93181062307b
@@@ -162,7 -162,7 +162,7 @@@ V_c(\rij) = f \frac{q_i q_j}{\epsr \rij
  \label{eqn:vcoul}
  \eeq
  See also \figref{coul}, where $f = \frac{1}{4\pi \varepsilon_0} =
138.935\,485$ (see \chref{defunits})
\electricConvFactorValue$ (see \chref{defunits})
  
  \begin{figure}
  \centerline{\includegraphics[width=8cm]{plots/vcrf}}
@@@ -854,7 -854,7 +854,7 @@@ or four~\cite{Jorgensen2005a} cosine te
  In {\gromacs} the four term function is implemented:
  \beq
  V_{F} (\phi_{ijkl}) ~=~ \frac{1}{2} \left[C_1(1+\cos(\phi)) + C_2(
- 1-\cos(2\phi)) + C_3(1+\cos(3\phi)) + C_4(1+\cos(4\phi))\right],
+ 1-\cos(2\phi)) + C_3(1+\cos(3\phi)) + C_4(1-\cos(4\phi))\right],
  \eeq
  %\ifthenelse{\equal{\gmxlite}{1}}{}{
  Internally, {\gromacs}
@@@ -1755,7 -1755,7 +1755,7 @@@ of which the charge varies with $\LAM$ 
  V_c &=& \frac{f}{\epsrf \rij}\left[\LL q_i^A q_j^A + \LAM\, q_i^B q_j^B\right] \\
  \dvdl{V_c}&=& \frac{f}{\epsrf \rij}\left[- q_i^A q_j^A + q_i^B q_j^B\right]
  \eea
- where $f = \frac{1}{4\pi \varepsilon_0} = 138.935\,485$ (see \chref{defunits}).
+ where $f = \frac{1}{4\pi \varepsilon_0} = \electricConvFactorValue$ (see \chref{defunits}).
  
  \subsubsection{Coulomb interaction with \normindex{reaction field}}
  The Coulomb interaction including a reaction field, between two particles 
@@@ -2030,13 -2030,48 +2030,13 @@@ type selectors (termed {\tt vdwtype} an
  parameters, for a total of six non-bonded interaction parameters. See
  the User Guide for a complete description of these parameters.
  
 -The neighbor searching (NS) can be performed using a single-range, or a twin-range 
 -approach. Since the former is merely a special case of the latter, we will 
 -discuss the more general twin-range. In this case, NS is described by two
 -radii: {\tt rlist} and max({\tt rcoulomb},{\tt rvdw}).
 -Usually one builds the neighbor list every 10 time steps
 -or every 20 fs (parameter {\tt nstlist}). In the neighbor list, all interaction 
 -pairs that  fall within {\tt rlist} are stored. Furthermore, the 
 -interactions between pairs that do not
 -fall within {\tt rlist} but do fall within max({\tt rcoulomb},{\tt rvdw})
 -are computed during NS.  The
 -forces and energy are stored separately and added to short-range forces
 -at every time step between successive NS. If {\tt rlist} = 
 -max({\tt rcoulomb},{\tt rvdw}), no forces
 -are evaluated during neighbor list generation.
 -The \normindex{virial} is calculated from the sum of the short- and
 -long-range forces.
 -This means that the virial can be slightly asymmetrical at non-NS steps.
 -When mdrun is compiled to use mixed precision, the virial is almost always asymmetrical because the
 -off-diagonal elements are about as large as each element in the sum.
 -In most cases this is not really a problem, since the fluctuations in the
 -virial can be 2 orders of magnitude larger than the average.
 -
 -Except for the plain cut-off,
 -all of the interaction functions in \tabref{funcparm}
 -require that neighbor searching be done with a larger radius than the $r_c$
 +In the group cut-off scheme, all of the interaction functions in \tabref{funcparm}
 +require that neighbor searching be done with a radius at least as large as the $r_c$
  specified for the functional form, because of the use of charge groups.
  The extra radius is typically of the order of 0.25 nm (roughly the 
  largest distance between two atoms in a charge group plus the distance a 
  charge group can diffuse within neighbor list updates).
  
 -%If your charge groups are very large it may be interesting to turn off charge
 -%groups, by setting the option 
 -%{\tt bAtomList = yes} in your {\tt grompp.mdp} file.
 -%In this case only a small extra radius to account for diffusion needs to be 
 -%added (0.1 nm). Do not however use this together with the plain cut-off
 -%method, as it will generate large artifacts (\secref{cg}).
 -%In summary, there are four parameters that describe NS behavior:
 -%{\tt nstlist} (update frequency in number of time steps),
 -%{\tt bAtomList} (whether or not charge groups are used to generate neighbor list, the default is to use charge groups, so {\tt bAtomList = no}),
 -%{\tt rshort} and {\tt rlong} which are the two radii {\rs} and {\rl}
 -%described above.
 -
  \begin{table}[ht]
  \centering
  \begin{tabular}{|ll|l|}
index f2ca6ab21fa370d38d2f774d3ffbcb7e43b8c17d,0000000000000000000000000000000000000000..ec777c48970109a34d6f2641d1f5b7af485fa1ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,830 -1,0 +1,828 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * 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.
-     int         i, k, nfour;
++ * Copyright (c) 2013,2014,2015,2016, 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.
 + */
 +/*! \internal \file
 + * \brief
 + * Implements function to compute many autocorrelation functions
 + *
 + * \author David van der Spoel <david.vanderspoel@icm.uu.se>
 + * \ingroup module_correlationfunctions
 + */
 +#include "gmxpre.h"
 +
 +#include "autocorr.h"
 +
 +#include <stdio.h>
 +#include <string.h>
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/correlationfunctions/expfit.h"
 +#include "gromacs/correlationfunctions/integrate.h"
 +#include "gromacs/correlationfunctions/manyautocorrelation.h"
 +#include "gromacs/correlationfunctions/polynomials.h"
 +#include "gromacs/fileio/xvgr.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/utility/arraysize.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/real.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/stringutil.h"
 +
 +/*! \brief Shortcut macro to select modes. */
 +#define MODE(x) ((mode & (x)) == (x))
 +
 +typedef struct {
 +    unsigned long mode;
 +    int           nrestart, nout, P, fitfn;
 +    gmx_bool      bFour, bNormalize;
 +    real          tbeginfit, tendfit;
 +} t_acf;
 +
 +/*! \brief Global variable set true if initialization routines are called. */
 +static gmx_bool  bACFinit = FALSE;
 +
 +/*! \brief Data structure for storing command line variables. */
 +static t_acf     acf;
 +
 +enum {
 +    enNorm, enCos, enSin
 +};
 +
 +/*! \brief Routine to compute ACF using FFT. */
 +static void low_do_four_core(int nfour, int nframes, real c1[], real cfour[],
 +                             int nCos)
 +{
 +    int  i = 0;
 +
 +    switch (nCos)
 +    {
 +        case enNorm:
 +            for (i = 0; (i < nframes); i++)
 +            {
 +                cfour[i] = c1[i];
 +            }
 +            break;
 +        case enCos:
 +            for (i = 0; (i < nframes); i++)
 +            {
 +                cfour[i] = cos(c1[i]);
 +            }
 +            break;
 +        case enSin:
 +            for (i = 0; (i < nframes); i++)
 +            {
 +                cfour[i] = sin(c1[i]);
 +            }
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "nCos = %d, %s %d", nCos, __FILE__, __LINE__);
 +    }
 +
 +    many_auto_correl(1, nframes, nfour, &cfour);
 +}
 +
 +/*! \brief Routine to comput ACF without FFT. */
 +static void do_ac_core(int nframes, int nout,
 +                       real corr[], real c1[], int nrestart,
 +                       unsigned long mode)
 +{
 +    int     j, k, j3, jk3, m, n;
 +    real    ccc, cth;
 +    rvec    xj, xk;
 +
 +    if (nrestart < 1)
 +    {
 +        printf("WARNING: setting number of restarts to 1\n");
 +        nrestart = 1;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Starting do_ac_core: nframes=%d, nout=%d, nrestart=%d,mode=%lu\n",
 +                nframes, nout, nrestart, mode);
 +    }
 +
 +    for (j = 0; (j < nout); j++)
 +    {
 +        corr[j] = 0;
 +    }
 +
 +    /* Loop over starting points. */
 +    for (j = 0; (j < nframes); j += nrestart)
 +    {
 +        j3  = DIM*j;
 +
 +        /* Loop over the correlation length for this starting point */
 +        for (k = 0; (k < nout) && (j+k < nframes); k++)
 +        {
 +            jk3 = DIM*(j+k);
 +
 +            /* Switch over possible ACF types.
 +             * It might be more efficient to put the loops inside the switch,
 +             * but this is more clear, and save development time!
 +             */
 +            if (MODE(eacNormal))
 +            {
 +                corr[k] += c1[j]*c1[j+k];
 +            }
 +            else if (MODE(eacCos))
 +            {
 +                /* Compute the cos (phi(t)-phi(t+dt)) */
 +                corr[k] += cos(c1[j]-c1[j+k]);
 +            }
 +            else if (MODE(eacIden))
 +            {
 +                /* Check equality (phi(t)==phi(t+dt)) */
 +                corr[k] += (c1[j] == c1[j+k]) ? 1 : 0;
 +            }
 +            else if (MODE(eacP1) || MODE(eacP2) || MODE(eacP3))
 +            {
 +                unsigned int mmm;
 +
 +                for (m = 0; (m < DIM); m++)
 +                {
 +                    xj[m] = c1[j3+m];
 +                    xk[m] = c1[jk3+m];
 +                }
 +                cth = cos_angle(xj, xk);
 +
 +                if (cth-1.0 > 1.0e-15)
 +                {
 +                    printf("j: %d, k: %d, xj:(%g,%g,%g), xk:(%g,%g,%g)\n",
 +                           j, k, xj[XX], xj[YY], xj[ZZ], xk[XX], xk[YY], xk[ZZ]);
 +                }
 +                mmm = 1;
 +                if (MODE(eacP2))
 +                {
 +                    mmm = 2;
 +                }
 +                else if (MODE(eacP3))
 +                {
 +                    mmm = 3;
 +                }
 +                corr[k] += LegendreP(cth, mmm); /* 1.5*cth*cth-0.5; */
 +            }
 +            else if (MODE(eacRcross))
 +            {
 +                rvec xj, xk, rr;
 +                for (m = 0; (m < DIM); m++)
 +                {
 +                    xj[m] = c1[j3+m];
 +                    xk[m] = c1[jk3+m];
 +                }
 +                cprod(xj, xk, rr);
 +
 +                corr[k] += iprod(rr, rr);
 +            }
 +            else if (MODE(eacVector))
 +            {
 +                for (m = 0; (m < DIM); m++)
 +                {
 +                    xj[m] = c1[j3+m];
 +                    xk[m] = c1[jk3+m];
 +                }
 +                ccc = iprod(xj, xk);
 +
 +                corr[k] += ccc;
 +            }
 +            else
 +            {
 +                gmx_fatal(FARGS, "\nInvalid mode (%d) in do_ac_core", mode);
 +            }
 +        }
 +    }
 +    /* Correct for the number of points and copy results to the data array */
 +    for (j = 0; (j < nout); j++)
 +    {
 +        n     = (nframes-j+(nrestart-1))/nrestart;
 +        c1[j] = corr[j]/n;
 +    }
 +}
 +
 +/*! \brief Routine to normalize ACF, dividing by corr[0]. */
 +void normalize_acf(int nout, real corr[])
 +{
 +    int    j;
 +    double c0;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Before normalization\n");
 +        for (j = 0; (j < nout); j++)
 +        {
 +            fprintf(debug, "%5d  %10f\n", j, corr[j]);
 +        }
 +    }
 +
 +    /* Normalisation makes that c[0] = 1.0 and that other points are scaled
 +     * accordingly.
 +     */
 +    if (fabs(corr[0]) < 1e-5)
 +    {
 +        c0 = 1.0;
 +    }
 +    else
 +    {
 +        c0 = 1.0/corr[0];
 +    }
 +    for (j = 0; (j < nout); j++)
 +    {
 +        corr[j] *= c0;
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "After normalization\n");
 +        for (j = 0; (j < nout); j++)
 +        {
 +            fprintf(debug, "%5d  %10f\n", j, corr[j]);
 +        }
 +    }
 +}
 +
 +/*! \brief Routine that averages ACFs. */
 +void average_acf(gmx_bool bVerbose, int n, int nitem, real **c1)
 +{
 +    real c0;
 +    int  i, j;
 +
 +    if (bVerbose)
 +    {
 +        printf("Averaging correlation functions\n");
 +    }
 +
 +    for (j = 0; (j < n); j++)
 +    {
 +        c0 = 0;
 +        for (i = 0; (i < nitem); i++)
 +        {
 +            c0 += c1[i][j];
 +        }
 +        c1[0][j] = c0/nitem;
 +    }
 +}
 +
 +/*! \brief Normalize ACFs. */
 +void norm_and_scale_vectors(int nframes, real c1[], real scale)
 +{
 +    int   j, m;
 +    real *rij;
 +
 +    for (j = 0; (j < nframes); j++)
 +    {
 +        rij = &(c1[j*DIM]);
 +        unitv(rij, rij);
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            rij[m] *= scale;
 +        }
 +    }
 +}
 +
 +/*! \brief Debugging */
 +static void dump_tmp(char *s, int n, real c[])
 +{
 +    FILE *fp;
 +    int   i;
 +
 +    fp = gmx_ffopen(s, "w");
 +    for (i = 0; (i < n); i++)
 +    {
 +        fprintf(fp, "%10d  %10g\n", i, c[i]);
 +    }
 +    gmx_ffclose(fp);
 +}
 +
 +/*! \brief High level ACF routine. */
 +void do_four_core(unsigned long mode, int nfour, int nf2, int nframes,
 +                  real c1[], real csum[], real ctmp[])
 +{
 +    real   *cfour;
 +    char    buf[32];
 +    real    fac;
 +    int     j, m, m1;
 +
 +    snew(cfour, nfour);
 +
 +    if (MODE(eacNormal))
 +    {
 +        /********************************************
 +         *  N O R M A L
 +         ********************************************/
 +        low_do_four_core(nfour, nf2, c1, csum, enNorm);
 +    }
 +    else if (MODE(eacCos))
 +    {
 +        /***************************************************
 +         * C O S I N E
 +         ***************************************************/
 +        /* Copy the data to temp array. Since we need it twice
 +         * we can't overwrite original.
 +         */
 +        for (j = 0; (j < nf2); j++)
 +        {
 +            ctmp[j] = c1[j];
 +        }
 +
 +        /* Cosine term of AC function */
 +        low_do_four_core(nfour, nf2, ctmp, cfour, enCos);
 +        for (j = 0; (j < nf2); j++)
 +        {
 +            c1[j]  = cfour[j];
 +        }
 +
 +        /* Sine term of AC function */
 +        low_do_four_core(nfour, nf2, ctmp, cfour, enSin);
 +        for (j = 0; (j < nf2); j++)
 +        {
 +            c1[j]  += cfour[j];
 +            csum[j] = c1[j];
 +        }
 +    }
 +    else if (MODE(eacP2))
 +    {
 +        /***************************************************
 +         * Legendre polynomials
 +         ***************************************************/
 +        /* First normalize the vectors */
 +        norm_and_scale_vectors(nframes, c1, 1.0);
 +
 +        /* For P2 thingies we have to do six FFT based correls
 +         * First for XX^2, then for YY^2, then for ZZ^2
 +         * Then we have to do XY, YZ and XZ (counting these twice)
 +         * After that we sum them and normalise
 +         * P2(x) = (3 * cos^2 (x) - 1)/2
 +         * for unit vectors u and v we compute the cosine as the inner product
 +         * cos(u,v) = uX vX + uY vY + uZ vZ
 +         *
 +         *        oo
 +         *        /
 +         * C(t) = |  (3 cos^2(u(t'),u(t'+t)) - 1)/2 dt'
 +         *        /
 +         *        0
 +         *
 +         * For ACF we need:
 +         * P2(u(0),u(t)) = [3 * (uX(0) uX(t) +
 +         *                       uY(0) uY(t) +
 +         *                       uZ(0) uZ(t))^2 - 1]/2
 +         *               = [3 * ((uX(0) uX(t))^2 +
 +         *                       (uY(0) uY(t))^2 +
 +         *                       (uZ(0) uZ(t))^2 +
 +         *                 2(uX(0) uY(0) uX(t) uY(t)) +
 +         *                 2(uX(0) uZ(0) uX(t) uZ(t)) +
 +         *                 2(uY(0) uZ(0) uY(t) uZ(t))) - 1]/2
 +         *
 +         *               = [(3/2) * (<uX^2> + <uY^2> + <uZ^2> +
 +         *                         2<uXuY> + 2<uXuZ> + 2<uYuZ>) - 0.5]
 +         *
 +         */
 +
 +        /* Because of normalization the number of -0.5 to subtract
 +         * depends on the number of data points!
 +         */
 +        for (j = 0; (j < nf2); j++)
 +        {
 +            csum[j]  = -0.5*(nf2-j);
 +        }
 +
 +        /***** DIAGONAL ELEMENTS ************/
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            /* Copy the vector data in a linear array */
 +            for (j = 0; (j < nf2); j++)
 +            {
 +                ctmp[j]  = gmx::square(c1[DIM*j+m]);
 +            }
 +            if (debug)
 +            {
 +                sprintf(buf, "c1diag%d.xvg", m);
 +                dump_tmp(buf, nf2, ctmp);
 +            }
 +
 +            low_do_four_core(nfour, nf2, ctmp, cfour, enNorm);
 +
 +            if (debug)
 +            {
 +                sprintf(buf, "c1dfout%d.xvg", m);
 +                dump_tmp(buf, nf2, cfour);
 +            }
 +            fac = 1.5;
 +            for (j = 0; (j < nf2); j++)
 +            {
 +                csum[j] += fac*(cfour[j]);
 +            }
 +        }
 +        /******* OFF-DIAGONAL ELEMENTS **********/
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            /* Copy the vector data in a linear array */
 +            m1 = (m+1) % DIM;
 +            for (j = 0; (j < nf2); j++)
 +            {
 +                ctmp[j] = c1[DIM*j+m]*c1[DIM*j+m1];
 +            }
 +
 +            if (debug)
 +            {
 +                sprintf(buf, "c1off%d.xvg", m);
 +                dump_tmp(buf, nf2, ctmp);
 +            }
 +            low_do_four_core(nfour, nf2, ctmp, cfour, enNorm);
 +            if (debug)
 +            {
 +                sprintf(buf, "c1ofout%d.xvg", m);
 +                dump_tmp(buf, nf2, cfour);
 +            }
 +            fac = 3.0;
 +            for (j = 0; (j < nf2); j++)
 +            {
 +                csum[j] += fac*cfour[j];
 +            }
 +        }
 +    }
 +    else if (MODE(eacP1) || MODE(eacVector))
 +    {
 +        /***************************************************
 +         * V E C T O R & P1
 +         ***************************************************/
 +        if (MODE(eacP1))
 +        {
 +            /* First normalize the vectors */
 +            norm_and_scale_vectors(nframes, c1, 1.0);
 +        }
 +
 +        /* For vector thingies we have to do three FFT based correls
 +         * First for XX, then for YY, then for ZZ
 +         * After that we sum them and normalise
 +         */
 +        for (j = 0; (j < nf2); j++)
 +        {
 +            csum[j] = 0.0;
 +        }
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            /* Copy the vector data in a linear array */
 +            for (j = 0; (j < nf2); j++)
 +            {
 +                ctmp[j] = c1[DIM*j+m];
 +            }
 +            low_do_four_core(nfour, nf2, ctmp, cfour, enNorm);
 +            for (j = 0; (j < nf2); j++)
 +            {
 +                csum[j] += cfour[j];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "\nUnknown mode in do_autocorr (%d)", mode);
 +    }
 +
 +    sfree(cfour);
 +    for (j = 0; (j < nf2); j++)
 +    {
 +        c1[j] = csum[j]/(real)(nframes-j);
 +    }
 +}
 +
 +void low_do_autocorr(const char *fn, const gmx_output_env_t *oenv, const char *title,
 +                     int nframes, int nitem, int nout, real **c1,
 +                     real dt, unsigned long mode, int nrestart,
 +                     gmx_bool bAver, gmx_bool bNormalize,
 +                     gmx_bool bVerbose, real tbeginfit, real tendfit,
 +                     int eFitFn)
 +{
 +    FILE       *fp, *gp = NULL;
-     real        c0, sum, Ct2av, Ctav;
++    int         i, nfour;
 +    real       *csum;
 +    real       *ctmp, *fit;
-         c0 = std::log(static_cast<double>(nframes))/std::log(2.0);
-         k  = static_cast<int>(c0);
-         if (k < c0)
++    real        sum, Ct2av, Ctav;
 +    gmx_bool    bFour = acf.bFour;
 +
 +    /* Check flags and parameters */
 +    nout = get_acfnout();
 +    if (nout == -1)
 +    {
 +        nout = acf.nout = (nframes+1)/2;
 +    }
 +    else if (nout > nframes)
 +    {
 +        nout = nframes;
 +    }
 +
 +    if (MODE(eacCos) && MODE(eacVector))
 +    {
 +        gmx_fatal(FARGS, "Incompatible options bCos && bVector (%s, %d)",
 +                  __FILE__, __LINE__);
 +    }
 +    if ((MODE(eacP3) || MODE(eacRcross)) && bFour)
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "Can't combine mode %lu with FFT, turning off FFT\n", mode);
 +        }
 +        bFour = FALSE;
 +    }
 +    if (MODE(eacNormal) && MODE(eacVector))
 +    {
 +        gmx_fatal(FARGS, "Incompatible mode bits: normal and vector (or Legendre)");
 +    }
 +
 +    /* Print flags and parameters */
 +    if (bVerbose)
 +    {
 +        printf("Will calculate %s of %d thingies for %d frames\n",
 +               title ? title : "autocorrelation", nitem, nframes);
 +        printf("bAver = %s, bFour = %s bNormalize= %s\n",
 +               gmx::boolToString(bAver), gmx::boolToString(bFour),
 +               gmx::boolToString(bNormalize));
 +        printf("mode = %lu, dt = %g, nrestart = %d\n", mode, dt, nrestart);
 +    }
 +    if (bFour)
 +    {
-             k++;
++        /* For FTT corr., we need to pad the data with at least nframes zeros */
++        nfour = 2;
++        while (2*nframes > nfour)
 +        {
-         k++;
-         nfour = 1<<k;
++            nfour *= 2;
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Using FFT to calculate %s, #points for FFT = %d\n",
 +                    title, nfour);
 +        }
 +
 +        /* Allocate temp arrays */
 +        snew(csum, nfour);
 +        snew(ctmp, nfour);
 +    }
 +    else
 +    {
 +        nfour = 0; /* To keep the compiler happy */
 +        snew(csum, nframes);
 +        snew(ctmp, nframes);
 +    }
 +
 +    /* Loop over items (e.g. molecules or dihedrals)
 +     * In this loop the actual correlation functions are computed, but without
 +     * normalizing them.
 +     */
 +    for (int i = 0; i < nitem; i++)
 +    {
 +        if (bVerbose && (((i % 100) == 0) || (i == nitem-1)))
 +        {
 +            fprintf(stderr, "\rThingie %d", i+1);
 +        }
 +
 +        if (bFour)
 +        {
 +            do_four_core(mode, nfour, nframes, nframes, c1[i], csum, ctmp);
 +        }
 +        else
 +        {
 +            do_ac_core(nframes, nout, ctmp, c1[i], nrestart, mode);
 +        }
 +    }
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "\n");
 +    }
 +    sfree(ctmp);
 +    sfree(csum);
 +
 +    if (fn)
 +    {
 +        snew(fit, nout);
 +        fp = xvgropen(fn, title, "Time (ps)", "C(t)", oenv);
 +    }
 +    else
 +    {
 +        fit = NULL;
 +        fp  = NULL;
 +    }
 +    if (bAver)
 +    {
 +        if (nitem > 1)
 +        {
 +            average_acf(bVerbose, nframes, nitem, c1);
 +        }
 +
 +        if (bNormalize)
 +        {
 +            normalize_acf(nout, c1[0]);
 +        }
 +
 +        if (eFitFn != effnNONE)
 +        {
 +            fit_acf(nout, eFitFn, oenv, fn != NULL, tbeginfit, tendfit, dt, c1[0], fit);
 +            sum = print_and_integrate(fp, nout, dt, c1[0], fit, 1);
 +        }
 +        else
 +        {
 +            sum = print_and_integrate(fp, nout, dt, c1[0], NULL, 1);
 +        }
 +        if (bVerbose)
 +        {
 +            printf("Correlation time (integral over corrfn): %g (ps)\n", sum);
 +        }
 +    }
 +    else
 +    {
 +        /* Not averaging. Normalize individual ACFs */
 +        Ctav = Ct2av = 0;
 +        if (debug)
 +        {
 +            gp = xvgropen("ct-distr.xvg", "Correlation times", "item", "time (ps)", oenv);
 +        }
 +        for (i = 0; i < nitem; i++)
 +        {
 +            if (bNormalize)
 +            {
 +                normalize_acf(nout, c1[i]);
 +            }
 +            if (eFitFn != effnNONE)
 +            {
 +                fit_acf(nout, eFitFn, oenv, fn != NULL, tbeginfit, tendfit, dt, c1[i], fit);
 +                sum = print_and_integrate(fp, nout, dt, c1[i], fit, 1);
 +            }
 +            else
 +            {
 +                sum = print_and_integrate(fp, nout, dt, c1[i], NULL, 1);
 +                if (debug)
 +                {
 +                    fprintf(debug,
 +                            "CORRelation time (integral over corrfn %d): %g (ps)\n",
 +                            i, sum);
 +                }
 +            }
 +            Ctav  += sum;
 +            Ct2av += sum*sum;
 +            if (debug)
 +            {
 +                fprintf(gp, "%5d  %.3f\n", i, sum);
 +            }
 +        }
 +        if (debug)
 +        {
 +            xvgrclose(gp);
 +        }
 +        if (nitem > 1)
 +        {
 +            Ctav  /= nitem;
 +            Ct2av /= nitem;
 +            printf("Average correlation time %.3f Std. Dev. %.3f Error %.3f (ps)\n",
 +                   Ctav, std::sqrt((Ct2av - gmx::square(Ctav))),
 +                   std::sqrt((Ct2av - gmx::square(Ctav))/(nitem-1)));
 +        }
 +    }
 +    if (fp)
 +    {
 +        xvgrclose(fp);
 +    }
 +    sfree(fit);
 +}
 +
 +/*! \brief Legend for selecting Legendre polynomials. */
 +static const char *Leg[]   = { NULL, "0", "1", "2", "3", NULL };
 +
 +t_pargs *add_acf_pargs(int *npargs, t_pargs *pa)
 +{
 +    t_pargs  acfpa[] = {
 +        { "-acflen",     FALSE, etINT,  {&acf.nout},
 +          "Length of the ACF, default is half the number of frames" },
 +        { "-normalize", FALSE, etBOOL, {&acf.bNormalize},
 +          "Normalize ACF" },
 +        { "-fftcorr",  FALSE, etBOOL, {&acf.bFour},
 +          "HIDDENUse fast fourier transform for correlation function" },
 +        { "-nrestart", FALSE, etINT,  {&acf.nrestart},
 +          "HIDDENNumber of frames between time origins for ACF when no FFT is used" },
 +        { "-P",        FALSE, etENUM, {Leg},
 +          "Order of Legendre polynomial for ACF (0 indicates none)" },
 +        { "-fitfn",    FALSE, etENUM, {s_ffn},
 +          "Fit function" },
 +        { "-beginfit", FALSE, etREAL, {&acf.tbeginfit},
 +          "Time where to begin the exponential fit of the correlation function" },
 +        { "-endfit",   FALSE, etREAL, {&acf.tendfit},
 +          "Time where to end the exponential fit of the correlation function, -1 is until the end" },
 +    };
 +    t_pargs *ppa;
 +    int      i, npa;
 +
 +    npa = asize(acfpa);
 +    snew(ppa, *npargs+npa);
 +    for (i = 0; (i < *npargs); i++)
 +    {
 +        ppa[i] = pa[i];
 +    }
 +    for (i = 0; (i < npa); i++)
 +    {
 +        ppa[*npargs+i] = acfpa[i];
 +    }
 +    (*npargs) += npa;
 +
 +    acf.mode       = 0;
 +    acf.nrestart   = 1;
 +    acf.nout       = -1;
 +    acf.P          = 0;
 +    acf.fitfn      = effnEXP1;
 +    acf.bFour      = TRUE;
 +    acf.bNormalize = TRUE;
 +    acf.tbeginfit  = 0.0;
 +    acf.tendfit    = -1;
 +
 +    bACFinit = TRUE;
 +
 +    return ppa;
 +}
 +
 +void do_autocorr(const char *fn, const gmx_output_env_t *oenv, const char *title,
 +                 int nframes, int nitem, real **c1,
 +                 real dt, unsigned long mode, gmx_bool bAver)
 +{
 +    if (!bACFinit)
 +    {
 +        printf("ACF data structures have not been initialised. Call add_acf_pargs\n");
 +    }
 +
 +    /* Handle enumerated types */
 +    sscanf(Leg[0], "%d", &acf.P);
 +    acf.fitfn = sffn2effn(s_ffn);
 +
 +    switch (acf.P)
 +    {
 +        case 1:
 +            mode = mode | eacP1;
 +            break;
 +        case 2:
 +            mode = mode | eacP2;
 +            break;
 +        case 3:
 +            mode = mode | eacP3;
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    low_do_autocorr(fn, oenv, title, nframes, nitem, acf.nout, c1, dt, mode,
 +                    acf.nrestart, bAver, acf.bNormalize,
 +                    bDebugMode(), acf.tbeginfit, acf.tendfit,
 +                    acf.fitfn);
 +}
 +
 +int get_acfnout(void)
 +{
 +    if (!bACFinit)
 +    {
 +        gmx_fatal(FARGS, "ACF data not initialized yet");
 +    }
 +
 +    return acf.nout;
 +}
 +
 +int get_acffitfn(void)
 +{
 +    if (!bACFinit)
 +    {
 +        gmx_fatal(FARGS, "ACF data not initialized yet");
 +    }
 +
 +    return sffn2effn(s_ffn);
 +}
index 71869f43ec822cd0fcee44f5df10f8298d94ada4,0000000000000000000000000000000000000000..b05dd6619068df6a1a2a675d3ce3be052f21c772
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-  * Copyright (c) 2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
-                     c[i][j] = out[j][0]/ndata;
++ * Copyright (c) 2014,2015,2016, 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.
 + */
 +/*! \internal \file
 + * \brief
 + * Implements function to compute many autocorrelation functions
 + *
 + * \author David van der Spoel <david.vanderspoel@icm.uu.se>
 + * \ingroup module_correlationfunctions
 + */
 +#include "gmxpre.h"
 +
 +#include "manyautocorrelation.h"
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fft/fft.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/gmxomp.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +int many_auto_correl(int nfunc, int ndata, int nfft, real **c)
 +{
 +    #pragma omp parallel
 +    {
 +        try
 +        {
 +            typedef real complex[2];
 +            int          i, j;
 +            gmx_fft_t    fft1;
 +            complex     *in, *out;
 +            int          i0, i1;
 +            int          nthreads, thread_id;
 +
 +            nthreads  = gmx_omp_get_max_threads();
 +            thread_id = gmx_omp_get_thread_num();
 +            if ((0 == thread_id))
 +            {
 +                // fprintf(stderr, "There are %d threads for correlation functions\n", nthreads);
 +            }
 +            i0 = thread_id*nfunc/nthreads;
 +            i1 = std::min(nfunc, (thread_id+1)*nfunc/nthreads);
 +
 +            gmx_fft_init_1d(&fft1, nfft, GMX_FFT_FLAG_CONSERVATIVE);
 +            /* Allocate temporary arrays */
 +            snew(in, nfft);
 +            snew(out, nfft);
 +            for (i = i0; (i < i1); i++)
 +            {
 +                for (j = 0; j < ndata; j++)
 +                {
 +                    in[j][0] = c[i][j];
 +                    in[j][1] = 0;
 +                }
 +                for (; (j < nfft); j++)
 +                {
 +                    in[j][0] = in[j][1] = 0;
 +                }
 +
 +                gmx_fft_1d(fft1, GMX_FFT_BACKWARD, (void *)in, (void *)out);
 +                for (j = 0; j < nfft; j++)
 +                {
 +                    in[j][0] = (out[j][0]*out[j][0] + out[j][1]*out[j][1])/nfft;
 +                    in[j][1] = 0;
 +                }
 +                for (; (j < nfft); j++)
 +                {
 +                    in[j][0] = in[j][1] = 0;
 +                }
 +
 +                gmx_fft_1d(fft1, GMX_FFT_FORWARD, (void *)in, (void *)out);
 +                for (j = 0; (j < nfft); j++)
 +                {
++                    c[i][j] = out[j][0];
 +                }
 +            }
 +            /* Free the memory */
 +            gmx_fft_destroy(fft1);
 +            sfree(in);
 +            sfree(out);
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +    // gmx_fft_cleanup();
 +    return 0;
 +}
index 5631d3f43550dd7e4b608eb02909bb4e977991dd,c8ee6edfb942e1646043b3f7676c78e5e3873cc9..561ecb99e68ddaf86cf5aca407ba176884bbb05a
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
-  * Copyright (c) 2014,2015, by the GROMACS development team, led by
 - * Copyright (c) 2014,2016, by the GROMACS development team, led by
++ * Copyright (c) 2014,2015,2016, 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.
  
  #include <cmath>
  
 +#include <memory>
 +
  #include <gtest/gtest.h>
  
  #include "gromacs/correlationfunctions/expfit.h"
  #include "gromacs/fft/fft.h"
 -#include "gromacs/legacyheaders/oenv.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/smalloc.h"
 -#include "gromacs/utility/uniqueptr.h"
  
  #include "testutils/refdata.h"
  #include "testutils/testasserts.h"
@@@ -67,7 -67,7 +67,7 @@@ namespac
  {
  
  //! Definition of pointer to class containing test data.
 -typedef gmx_unique_ptr<CorrelationDataSet>::type CorrelationDataSetPointer;
 +typedef std::unique_ptr<CorrelationDataSet> CorrelationDataSetPointer;
  
  class AutocorrTest : public ::testing::Test
  {
@@@ -85,7 -85,7 +85,7 @@@
          AutocorrTest( )
              : checker_(refData_.rootChecker())
          {
 -#ifdef GMX_DOUBLE
 +#if GMX_DOUBLE
              checker_.setDefaultTolerance(test::relativeToleranceAsFloatingPoint(1, 1e-6));
  #else
              checker_.setDefaultTolerance(test::relativeToleranceAsFloatingPoint(1, 1e-3));
  
          static void TearDownTestCase()
          {
              sfree(tempArgs_);
              tempArgs_ = NULL;
              gmx_fft_cleanup();
          }
  
-         void test(unsigned long mode)
+         void test(unsigned long mode, bool bNormalize)
          {
-             bool              bAverage      = false;
-             bool              bNormalize    = true;
+             bool              bAverage      = true;
              bool              bVerbose      = false;
              int               nrRestart     = 1;
              int               dim           = getDim(mode);
                              effnNONE);
  
              double testResult = 0;
-             for (int i = 0; i < nrFrames_; i++)
+             for (int i = 0; i < get_acfnout(); i++)
              {
                  testResult += result[i];
              }
-             checker_.checkSequenceArray(nrFrames_, ptr,
+             checker_.checkSequenceArray(get_acfnout(), ptr,
                                          "AutocorrelationFunction");
              checker_.checkReal(testResult, "Integral");
          }
@@@ -182,47 -180,52 +180,52 @@@ t_pargs                   * AutocorrTes
  
  TEST_F (AutocorrTest, EacNormal)
  {
-     test(eacNormal);
+     test(eacNormal, true);
+ }
+ TEST_F (AutocorrTest, EacNoNormalize)
+ {
+     test(eacNormal, false);
  }
  
  TEST_F (AutocorrTest, EacCos)
  {
-     test(eacCos);
+     test(eacCos, true);
  }
  
  TEST_F (AutocorrTest, EacVector)
  {
-     test(eacVector);
+     test(eacVector, true);
  }
  
  TEST_F (AutocorrTest, EacRcross)
  {
-     test(eacRcross);
+     test(eacRcross, true);
  }
  
  TEST_F (AutocorrTest, EacP0)
  {
-     test(eacP0);
+     test(eacP0, true);
  }
  
  TEST_F (AutocorrTest, EacP1)
  {
-     test(eacP1);
+     test(eacP1, true);
  }
  
  TEST_F (AutocorrTest, EacP2)
  {
-     test(eacP2);
+     test(eacP2, true);
  }
  
  TEST_F (AutocorrTest, EacP3)
  {
-     test(eacP3);
+     test(eacP3, true);
  }
  
  TEST_F (AutocorrTest, EacP4)
  {
-     test(eacP4);
+     test(eacP4, true);
  }
  
  
index 14be36b73a570e4c57bec4f1948b00824af45eb5,2203d1dbac27c245bffb15bc1d0a3ef98b5abdcd..18cc875ea7fbc6749d38b51c0da29e626360fb27
  
  #include "gromacs/domdec/domdec.h"
  #include "gromacs/domdec/domdec_network.h"
 -#include "gromacs/legacyheaders/calcgrid.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/md_logging.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/sim_util.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/domdec/domdec_struct.h"
 +#include "gromacs/fft/calcgrid.h"
 +#include "gromacs/gmxlib/md_logging.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/math/functions.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/forcerec.h"
  #include "gromacs/mdlib/nbnxn_gpu_data_mgmt.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/timing/wallcycle.h"
  #include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/smalloc.h"
  
  #include "pme-internal.h"
@@@ -79,6 -74,8 +79,6 @@@
  struct pme_setup_t {
      real              rcut_coulomb;    /**< Coulomb cut-off                              */
      real              rlist;           /**< pair-list cut-off                            */
 -    real              rlistlong;       /**< LR pair-list cut-off                         */
 -    int               nstcalclr;       /**< frequency of evaluating long-range forces for group scheme */
      real              spacing;         /**< (largest) PME grid spacing                   */
      ivec              grid;            /**< the PME grid dimensions                      */
      real              grid_efficiency; /**< ineffiency factor for non-uniform grids <= 1 */
@@@ -126,6 -123,7 +126,6 @@@ struct pme_load_balancing_t 
      real         cut_spacing;        /**< the minimum cutoff / PME grid spacing ratio */
      real         rcut_vdw;           /**< Vdw cutoff (does not change) */
      real         rcut_coulomb_start; /**< Initial electrostatics cutoff */
 -    int          nstcalclr_start;    /**< Initial electrostatics cutoff */
      real         rbuf_coulomb;       /**< the pairlist buffer size */
      real         rbuf_vdw;           /**< the pairlist buffer size */
      matrix       box_start;          /**< the initial simulation box */
@@@ -185,8 -183,22 +185,8 @@@ void pme_loadbal_init(pme_load_balancin
      }
      else
      {
 -        if (ic->rcoulomb > ic->rlist)
 -        {
 -            pme_lb->rbuf_coulomb = ic->rlistlong - ic->rcoulomb;
 -        }
 -        else
 -        {
 -            pme_lb->rbuf_coulomb = ic->rlist - ic->rcoulomb;
 -        }
 -        if (ic->rvdw > ic->rlist)
 -        {
 -            pme_lb->rbuf_vdw = ic->rlistlong - ic->rvdw;
 -        }
 -        else
 -        {
 -            pme_lb->rbuf_vdw = ic->rlist - ic->rvdw;
 -        }
 +        pme_lb->rbuf_coulomb = ic->rlist - ic->rcoulomb;
 +        pme_lb->rbuf_vdw     = ic->rlist - ic->rvdw;
      }
  
      copy_mat(box, pme_lb->box_start);
  
      pme_lb->rcut_vdw                 = ic->rvdw;
      pme_lb->rcut_coulomb_start       = ir->rcoulomb;
 -    pme_lb->nstcalclr_start          = ir->nstcalclr;
  
      pme_lb->cur                      = 0;
      pme_lb->setup[0].rcut_coulomb    = ic->rcoulomb;
      pme_lb->setup[0].rlist           = ic->rlist;
 -    pme_lb->setup[0].rlistlong       = ic->rlistlong;
 -    pme_lb->setup[0].nstcalclr       = ir->nstcalclr;
      pme_lb->setup[0].grid[XX]        = ir->nkx;
      pme_lb->setup[0].grid[YY]        = ir->nky;
      pme_lb->setup[0].grid[ZZ]        = ir->nkz;
      pme_lb->cycles_n = 0;
      pme_lb->cycles_c = 0;
  
 +    if (!wallcycle_have_counter())
 +    {
 +        md_print_warn(cr, fp_log, "NOTE: Cycle counters unsupported or not enabled in kernel. Cannot use PME-PP balancing.\n");
 +    }
 +
      /* Tune with GPUs and/or separate PME ranks.
       * When running only on a CPU without PME ranks, PME tuning will only help
       * with small numbers of atoms in the cut-off sphere.
@@@ -355,12 -365,47 +355,12 @@@ static gmx_bool pme_loadbal_increase_cu
      if (pme_lb->cutoff_scheme == ecutsVERLET)
      {
          set->rlist        = set->rcut_coulomb + pme_lb->rbuf_coulomb;
 -        /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */
 -        set->rlistlong    = set->rlist;
      }
      else
      {
          tmpr_coulomb          = set->rcut_coulomb + pme_lb->rbuf_coulomb;
          tmpr_vdw              = pme_lb->rcut_vdw + pme_lb->rbuf_vdw;
          set->rlist            = std::min(tmpr_coulomb, tmpr_vdw);
 -        set->rlistlong        = std::max(tmpr_coulomb, tmpr_vdw);
 -
 -        /* Set the long-range update frequency */
 -        if (set->rlist == set->rlistlong)
 -        {
 -            /* No long-range interactions if the short-/long-range cutoffs are identical */
 -            set->nstcalclr = 0;
 -        }
 -        else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1)
 -        {
 -            /* We were not doing long-range before, but now we are since rlist!=rlistlong */
 -            set->nstcalclr = 1;
 -        }
 -        else
 -        {
 -            /* We were already doing long-range interactions from the start */
 -            if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start)
 -            {
 -                /* We were originally doing long-range VdW-only interactions.
 -                 * If rvdw is still longer than rcoulomb we keep the original nstcalclr,
 -                 * but if the coulomb cutoff has become longer we should update the long-range
 -                 * part every step.
 -                 */
 -                set->nstcalclr = (tmpr_vdw > tmpr_coulomb) ? pme_lb->nstcalclr_start : 1;
 -            }
 -            else
 -            {
 -                /* We were not doing any long-range interaction from the start,
 -                 * since it is not possible to do twin-range coulomb for the PME interaction.
 -                 */
 -                set->nstcalclr = 1;
 -            }
 -        }
      }
  
      set->spacing      = sp;
@@@ -515,7 -560,7 +515,7 @@@ pme_load_balance(pme_load_balancing_
                   t_commrec                 *cr,
                   FILE                      *fp_err,
                   FILE                      *fp_log,
 -                 t_inputrec                *ir,
 +                 const t_inputrec          *ir,
                   t_state                   *state,
                   double                     cycles,
                   interaction_const_t       *ic,
      set = &pme_lb->setup[pme_lb->cur];
      set->count++;
  
 -    rtab = ir->rlistlong + ir->tabext;
 +    rtab = ir->rlist + ir->tabext;
  
      if (set->count % 2 == 1)
      {
               * better overal performance can be obtained with a slightly
               * shorter cut-off and better DD load balancing.
               */
 -            set_dd_dlb_max_cutoff(cr, pme_lb->setup[pme_lb->fastest].rlistlong);
 +            set_dd_dlb_max_cutoff(cr, pme_lb->setup[pme_lb->fastest].rlist);
          }
      }
      cycles_fast = pme_lb->setup[pme_lb->fastest].cycles;
  
              if (OK && ir->ePBC != epbcNONE)
              {
 -                OK = (sqr(pme_lb->setup[pme_lb->cur+1].rlistlong)
 +                OK = (gmx::square(pme_lb->setup[pme_lb->cur+1].rlist)
                        <= max_cutoff2(ir->ePBC, state->box));
                  if (!OK)
                  {
                  if (DOMAINDECOMP(cr))
                  {
                      OK = change_dd_cutoff(cr, state, ir,
 -                                          pme_lb->setup[pme_lb->cur].rlistlong);
 +                                          pme_lb->setup[pme_lb->cur].rlist);
                      if (!OK)
                      {
                          /* Failed: do not use this setup */
  
      if (DOMAINDECOMP(cr) && pme_lb->stage > 0)
      {
 -        OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong);
 +        OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlist);
          if (!OK)
          {
              /* For some reason the chosen cut-off is incompatible with DD.
  
      ic->rcoulomb     = set->rcut_coulomb;
      ic->rlist        = set->rlist;
 -    ic->rlistlong    = set->rlistlong;
 -    ir->nstcalclr    = set->nstcalclr;
      ic->ewaldcoeff_q = set->ewaldcoeff_q;
      /* TODO: centralize the code that sets the potentials shifts */
      if (ic->coulomb_modifier == eintmodPOTSHIFT)
      {
 -        ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb);
 +        ic->sh_ewald = std::erfc(ic->ewaldcoeff_q*ic->rcoulomb);
      }
      if (EVDW_PME(ic->vdwtype))
      {
          {
              real       crc2;
  
 -            ic->dispersion_shift.cpot = -std::pow(static_cast<double>(ic->rvdw), -6.0);
 -            ic->repulsion_shift.cpot  = -std::pow(static_cast<double>(ic->rvdw), -12.0);
 +            ic->dispersion_shift.cpot = -1.0/gmx::power6(static_cast<double>(ic->rvdw));
 +            ic->repulsion_shift.cpot  = -1.0/gmx::power12(static_cast<double>(ic->rvdw));
              ic->sh_invrc6             = -ic->dispersion_shift.cpot;
 -            crc2                      = sqr(ic->ewaldcoeff_lj*ic->rvdw);
 -            ic->sh_lj_ewald           = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*std::pow(static_cast<double>(ic->rvdw), -6.0);
 +            crc2                      = gmx::square(ic->ewaldcoeff_lj*ic->rvdw);
 +            ic->sh_lj_ewald           = (std::exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)/gmx::power6(ic->rvdw);
          }
      }
  
       * texture objects are used), but as this is initialization code, there
       * is not point in complicating things.
       */
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
      if (PAR(cr) && use_GPU(nbv))
      {
          gmx_barrier(cr);
@@@ -867,7 -914,7 +867,7 @@@ void pme_loadbal_do(pme_load_balancing_
                      t_commrec            *cr,
                      FILE                 *fp_err,
                      FILE                 *fp_log,
 -                    t_inputrec           *ir,
 +                    const t_inputrec     *ir,
                      t_forcerec           *fr,
                      t_state              *state,
                      gmx_wallcycle_t       wcycle,
      n_prev      = pme_lb->cycles_n;
      cycles_prev = pme_lb->cycles_c;
      wallcycle_get(wcycle, ewcSTEP, &pme_lb->cycles_n, &pme_lb->cycles_c);
-     if (pme_lb->cycles_n == 0)
+     /* Before the first step we haven't done any steps yet.
+      * Also handle cases where ir->init_step % ir->nstlist != 0.
+      */
+     if (pme_lb->cycles_n < ir->nstlist)
      {
-         /* Before the first step we haven't done any steps yet */
          return;
      }
      /* Sanity check, we expect nstlist cycle counts */
      if (pme_lb->cycles_n - n_prev != ir->nstlist)
      {
-         /* We could return here, but it's safer to issue and error and quit */
+         /* We could return here, but it's safer to issue an error and quit */
          gmx_incons("pme_loadbal_do called at an interval != nstlist");
      }
  
               * This also ensures that we won't disable the currently
               * optimal setting during a second round of PME balancing.
               */
 -            set_dd_dlb_max_cutoff(cr, fr->ic->rlistlong);
 +            set_dd_dlb_max_cutoff(cr, fr->ic->rlist);
          }
      }
  
          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;
  
@@@ -1021,6 -1072,23 +1024,6 @@@ static int pme_grid_points(const pme_se
      return setup->grid[XX]*setup->grid[YY]*setup->grid[ZZ];
  }
  
 -/*! \brief Retuern the largest short-range list cut-off radius */
 -static real pme_loadbal_rlist(const pme_setup_t *setup)
 -{
 -    /* With the group cut-off scheme we can have twin-range either
 -     * for Coulomb or for VdW, so we need a check here.
 -     * With the Verlet cut-off scheme rlist=rlistlong.
 -     */
 -    if (setup->rcut_coulomb > setup->rlist)
 -    {
 -        return setup->rlistlong;
 -    }
 -    else
 -    {
 -        return setup->rlist;
 -    }
 -}
 -
  /*! \brief Print one load-balancing setting */
  static void print_pme_loadbal_setting(FILE              *fplog,
                                        const char        *name,
      fprintf(fplog,
              "   %-7s %6.3f nm %6.3f nm     %3d %3d %3d   %5.3f nm  %5.3f nm\n",
              name,
 -            setup->rcut_coulomb, pme_loadbal_rlist(setup),
 +            setup->rcut_coulomb, setup->rlist,
              setup->grid[XX], setup->grid[YY], setup->grid[ZZ],
              setup->spacing, 1/setup->ewaldcoeff_q);
  }
@@@ -1043,8 -1111,8 +1046,8 @@@ static void print_pme_loadbal_settings(
      double     pp_ratio, grid_ratio;
      real       pp_ratio_temporary;
  
 -    pp_ratio_temporary = pme_loadbal_rlist(&pme_lb->setup[pme_lb->cur])/pme_loadbal_rlist(&pme_lb->setup[0]);
 -    pp_ratio           = std::pow(static_cast<double>(pp_ratio_temporary), 3.0);
 +    pp_ratio_temporary = pme_lb->setup[pme_lb->cur].rlist / pme_lb->setup[0].rlist;
 +    pp_ratio           = gmx::power3(pp_ratio_temporary);
      grid_ratio         = pme_grid_points(&pme_lb->setup[pme_lb->cur])/
          (double)pme_grid_points(&pme_lb->setup[0]);
  
index fe9c9072ed54594e91878ec9ca2cd3f2ca1204b5,0000000000000000000000000000000000000000..c9d4bb53de7afff883d10b10935feced9c8ffaf1
mode 100644,000000..100644
--- /dev/null
@@@ -1,437 -1,0 +1,436 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * 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.
-         srenew(y, 3);
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include <cmath>
 +#include <cstdio>
 +#include <cstdlib>
 +#include <cstring>
 +
 +#include <algorithm>
 +
 +#include "gromacs/commandline/viewit.h"
 +#include "gromacs/correlationfunctions/expfit.h"
 +#include "gromacs/correlationfunctions/integrate.h"
 +#include "gromacs/fft/fft.h"
 +#include "gromacs/fileio/xvgr.h"
 +#include "gromacs/gmxana/gmx_ana.h"
 +#include "gromacs/gmxana/gstat.h"
 +#include "gromacs/math/gmxcomplex.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/utility/arraysize.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/pleasecite.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +/* Determines at which point in the array the fit should start */
 +int calc_nbegin(int nx, real x[], real tbegin)
 +{
 +    int  nbegin;
 +
 +    /* Assume input x is sorted */
 +    for (nbegin = 0; (nbegin < nx) && (x[nbegin] <= tbegin); nbegin++)
 +    {
 +        ;
 +    }
 +    if ((nbegin == nx) || (nbegin == 0))
 +    {
 +        gmx_fatal(FARGS, "Begin time %f not in x-domain [%f through %f]\n",
 +                  tbegin, x[0], x[nx-1]);
 +    }
 +
 +    /* Take the one closest to tbegin */
 +    if (std::abs(x[nbegin]-tbegin) > std::abs(x[nbegin-1]-tbegin))
 +    {
 +        nbegin--;
 +    }
 +
 +    printf("nbegin = %d, x[nbegin] = %g, tbegin = %g\n",
 +           nbegin, x[nbegin], tbegin);
 +
 +    return nbegin;
 +}
 +
 +real numerical_deriv(int nx, real x[], real y[], real fity[], real combined[], real dy[],
 +                     real tendInt, int nsmooth)
 +{
 +    FILE *tmpfp;
 +    int   i, nbegin, i0, i1;
 +    real  fac, fx, fy, integralSmth;
 +
 +    nbegin = calc_nbegin(nx, x, tendInt);
 +    if (nsmooth == 0)
 +    {
 +        for (i = 0; (i < nbegin); i++)
 +        {
 +            combined[i] = y[i];
 +        }
 +        fac = y[nbegin]/fity[nbegin];
 +        printf("scaling fitted curve by %g\n", fac);
 +        for (i = nbegin; (i < nx); i++)
 +        {
 +            combined[i] = fity[i]*fac;
 +        }
 +    }
 +    else
 +    {
 +        i0 = std::max(0, nbegin);
 +        i1 = std::min(nx-1, nbegin+nsmooth);
 +        printf("Making smooth transition from %d through %d\n", i0, i1);
 +        for (i = 0; (i < i0); i++)
 +        {
 +            combined[i] = y[i];
 +        }
 +        for (i = i0; (i <= i1); i++)
 +        {
 +            fx = static_cast<real>(i1-i)/(i1-i0);
 +            fy = static_cast<real>(i-i0)/(i1-i0);
 +            if (debug)
 +            {
 +                fprintf(debug, "x: %g factors for smoothing: %10g %10g\n", x[i], fx, fy);
 +            }
 +            combined[i] = fx*y[i] + fy*fity[i];
 +        }
 +        for (i = i1+1; (i < nx); i++)
 +        {
 +            combined[i] = fity[i];
 +        }
 +    }
 +
 +    tmpfp        = gmx_ffopen("integral_smth.xvg", "w");
 +    integralSmth = print_and_integrate(tmpfp, nx, x[1]-x[0], combined, NULL, 1);
 +    printf("SMOOTH integral = %10.5e\n", integralSmth);
 +
 +    dy[0] = (combined[1]-combined[0])/(x[1]-x[0]);
 +    for (i = 1; (i < nx-1); i++)
 +    {
 +        dy[i] = (combined[i+1]-combined[i-1])/(x[i+1]-x[i-1]);
 +    }
 +    dy[nx-1] = (combined[nx-1]-combined[nx-2])/(x[nx-1]-x[nx-2]);
 +
 +    for (i = 0; (i < nx); i++)
 +    {
 +        dy[i] *= -1;
 +    }
 +
 +    return integralSmth;
 +}
 +
 +void do_four(const char *fn, const char *cn, int nx, real x[], real dy[],
 +             real eps0, real epsRF, const gmx_output_env_t *oenv)
 +{
 +    FILE      *fp, *cp;
 +    t_complex *tmp, gw, hw, kw;
 +    int        i, nnx, nxsav;
 +    real       fac, nu, dt, maxeps, numax;
 +    gmx_fft_t  fft;
 +    int        fftcode;
 +
 +    nxsav = nx;
 +    /*while ((dy[nx-1] == 0.0) && (nx > 0))
 +       nx--;*/
 +    if (nx == 0)
 +    {
 +        gmx_fatal(FARGS, "Empty dataset in %s, line %d!", __FILE__, __LINE__);
 +    }
 +
 +    nnx = 1;
 +    while (nnx < 2*nx)
 +    {
 +        nnx *= 2;
 +    }
 +
 +    snew(tmp, 2*nnx);
 +    printf("Doing FFT of %d points\n", nnx);
 +    for (i = 0; (i < nx); i++)
 +    {
 +        tmp[i].re = dy[i];
 +    }
 +    if ((fftcode = gmx_fft_init_1d_real(&fft, nnx,
 +                                        GMX_FFT_FLAG_NONE)) != 0)
 +    {
 +        gmx_fatal(FARGS, "gmx_fft_init_1d_real returned %d", fftcode);
 +    }
 +    if ((fftcode = gmx_fft_1d_real(fft, GMX_FFT_COMPLEX_TO_REAL,
 +                                   (void *)tmp, (void *)tmp)) != 0)
 +    {
 +        gmx_fatal(FARGS, "gmx_fft_1d_real returned %d", fftcode);
 +    }
 +    gmx_fft_destroy(fft);
 +    dt = x[1]-x[0];
 +    if (epsRF == 0)
 +    {
 +        fac = (eps0-1)/tmp[0].re;
 +    }
 +    else
 +    {
 +        fac = ((eps0-1)/(2*epsRF+eps0))/tmp[0].re;
 +    }
 +    fp     = xvgropen(fn, "Epsilon(\\8w\\4)", "Freq. (GHz)", "eps", oenv);
 +    cp     = xvgropen(cn, "Cole-Cole plot", "Eps'", "Eps''", oenv);
 +    maxeps = 0;
 +    numax  = 0;
 +    for (i = 0; (i < nxsav); i++)
 +    {
 +        if (epsRF == 0)
 +        {
 +            kw.re = 1+fac*tmp[i].re;
 +            kw.im = 1+fac*tmp[i].im;
 +        }
 +        else
 +        {
 +            gw     = rcmul(fac, tmp[i]);
 +            hw     = rcmul(2*epsRF, gw);
 +            hw.re += 1.0;
 +            gw.re  = 1.0 - gw.re;
 +            gw.im  = -gw.im;
 +            kw     = cdiv(hw, gw);
 +        }
 +        kw.im *= -1;
 +
 +        nu     = (i+1)*1000.0/(nnx*dt);
 +        if (kw.im > maxeps)
 +        {
 +            maxeps = kw.im;
 +            numax  = nu;
 +        }
 +
 +        fprintf(fp, "%10.5e  %10.5e  %10.5e\n", nu, kw.re, kw.im);
 +        fprintf(cp, "%10.5e  %10.5e\n", kw.re, kw.im);
 +    }
 +    printf("MAXEPS = %10.5e at frequency %10.5e GHz (tauD = %8.1f ps)\n",
 +           maxeps, numax, 1000/(2*M_PI*numax));
 +    xvgrclose(fp);
 +    xvgrclose(cp);
 +    sfree(tmp);
 +}
 +
 +int gmx_dielectric(int argc, char *argv[])
 +{
 +    const char       *desc[] = {
 +        "[THISMODULE] calculates frequency dependent dielectric constants",
 +        "from the autocorrelation function of the total dipole moment in",
 +        "your simulation. This ACF can be generated by [gmx-dipoles].",
 +        "The functional forms of the available functions are:",
 +        "",
 +        " * One parameter:    y = [EXP]-a[SUB]1[sub] x[exp],",
 +        " * Two parameters:   y = a[SUB]2[sub] [EXP]-a[SUB]1[sub] x[exp],",
 +        " * Three parameters: y = a[SUB]2[sub] [EXP]-a[SUB]1[sub] x[exp] + (1 - a[SUB]2[sub]) [EXP]-a[SUB]3[sub] x[exp].",
 +        "",
 +        "Start values for the fit procedure can be given on the command line.",
 +        "It is also possible to fix parameters at their start value, use [TT]-fix[tt]",
 +        "with the number of the parameter you want to fix.",
 +        "[PAR]",
 +        "Three output files are generated, the first contains the ACF,",
 +        "an exponential fit to it with 1, 2 or 3 parameters, and the",
 +        "numerical derivative of the combination data/fit.",
 +        "The second file contains the real and imaginary parts of the",
 +        "frequency-dependent dielectric constant, the last gives a plot",
 +        "known as the Cole-Cole plot, in which the imaginary",
 +        "component is plotted as a function of the real component.",
 +        "For a pure exponential relaxation (Debye relaxation) the latter",
 +        "plot should be one half of a circle."
 +    };
 +    t_filenm          fnm[] = {
 +        { efXVG, "-f", "dipcorr", ffREAD  },
 +        { efXVG, "-d", "deriv",  ffWRITE },
 +        { efXVG, "-o", "epsw",   ffWRITE },
 +        { efXVG, "-c", "cole",   ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +    gmx_output_env_t *oenv;
 +    int               i, j, nx, ny, nxtail, eFitFn, nfitparm;
 +    real              dt, integral, fitintegral, fac, rffac;
 +    double           *fitparms;
 +    double          **yd;
 +    real            **y;
 +    const char       *legend[] = { "Correlation", "Std. Dev.", "Fit", "Combined", "Derivative" };
 +    static int        fix      = 0, bX = 1, nsmooth = 3;
 +    static real       tendInt  = 5.0, tbegin = 5.0, tend = 500.0;
 +    static real       A        = 0.5, tau1 = 10.0, tau2 = 1.0, eps0 = 80, epsRF = 78.5, tail = 500.0;
 +    real              lambda;
 +    t_pargs           pa[] = {
 +        { "-x1",  FALSE, etBOOL, {&bX},
 +          "use first column as [IT]x[it]-axis rather than first data set" },
 +        { "-eint", FALSE, etREAL, {&tendInt},
 +          "Time to end the integration of the data and start to use the fit"},
 +        { "-bfit", FALSE, etREAL, {&tbegin},
 +          "Begin time of fit" },
 +        { "-efit", FALSE, etREAL, {&tend},
 +          "End time of fit" },
 +        { "-tail", FALSE, etREAL, {&tail},
 +          "Length of function including data and tail from fit" },
 +        { "-A", FALSE, etREAL, {&A},
 +          "Start value for fit parameter A" },
 +        { "-tau1", FALSE, etREAL, {&tau1},
 +          "Start value for fit parameter [GRK]tau[grk]1" },
 +        { "-tau2", FALSE, etREAL, {&tau2},
 +          "Start value for fit parameter [GRK]tau[grk]2" },
 +        { "-eps0", FALSE, etREAL, {&eps0},
 +          "[GRK]epsilon[grk]0 of your liquid" },
 +        { "-epsRF", FALSE, etREAL, {&epsRF},
 +          "[GRK]epsilon[grk] of the reaction field used in your simulation. A value of 0 means infinity." },
 +        { "-fix", FALSE, etINT,  {&fix},
 +          "Fix parameters at their start values, A (2), tau1 (1), or tau2 (4)" },
 +        { "-ffn",    FALSE, etENUM, {s_ffn},
 +          "Fit function" },
 +        { "-nsmooth", FALSE, etINT, {&nsmooth},
 +          "Number of points for smoothing" }
 +    };
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +    please_cite(stdout, "Spoel98a");
 +    printf("WARNING: non-polarizable models can never yield an infinite\n"
 +           "dielectric constant that is different from 1. This is incorrect\n"
 +           "in the reference given above (Spoel98a).\n\n");
 +
 +
 +    nx     = read_xvg(opt2fn("-f", NFILE, fnm), &yd, &ny);
 +    dt     = yd[0][1] - yd[0][0];
 +    nxtail = std::min(static_cast<int>(tail/dt), nx);
 +
 +    printf("Read data set containing %d colums and %d rows\n", ny, nx);
 +    printf("Assuming (from data) that timestep is %g, nxtail = %d\n",
 +           dt, nxtail);
 +    snew(y, 6);
 +    for (i = 0; (i < ny); i++)
 +    {
 +        snew(y[i], std::max(nx, nxtail));
 +    }
 +    for (i = 0; (i < nx); i++)
 +    {
 +        y[0][i] = yd[0][i];
 +        for (j = 1; (j < ny); j++)
 +        {
 +            y[j][i] = yd[j][i];
 +        }
 +    }
 +    if (nxtail > nx)
 +    {
 +        for (i = nx; (i < nxtail); i++)
 +        {
 +            y[0][i] = dt*i+y[0][0];
 +            for (j = 1; (j < ny); j++)
 +            {
 +                y[j][i] = 0.0;
 +            }
 +        }
 +        nx = nxtail;
 +    }
 +
 +
 +    /* We have read a file WITHOUT standard deviations, so we make our own... */
 +    if (ny == 2)
 +    {
 +        printf("Creating standard deviation numbers ...\n");
 +        snew(y[2], nx);
 +
 +        fac = 1.0/nx;
 +        for (i = 0; (i < nx); i++)
 +        {
 +            y[2][i] = fac;
 +        }
 +    }
 +
 +    eFitFn   = sffn2effn(s_ffn);
 +    nfitparm = effnNparams(eFitFn);
 +    snew(fitparms, 4);
 +    fitparms[0] = tau1;
 +    if (nfitparm > 1)
 +    {
 +        fitparms[1] = A;
 +    }
 +    if (nfitparm > 2)
 +    {
 +        fitparms[2] = tau2;
 +    }
 +
 +
 +    snew(y[3], nx);
 +    snew(y[4], nx);
 +    snew(y[5], nx);
 +
 +    integral = print_and_integrate(NULL, calc_nbegin(nx, y[0], tbegin),
 +                                   dt, y[1], NULL, 1);
 +    integral += do_lmfit(nx, y[1], y[2], dt, y[0], tbegin, tend,
 +                         oenv, TRUE, eFitFn, fitparms, fix, NULL);
 +    for (i = 0; i < nx; i++)
 +    {
 +        y[3][i] = fit_function(eFitFn, fitparms, y[0][i]);
 +    }
 +
 +    if (epsRF == 0)
 +    {
 +        /* This means infinity! */
 +        lambda = 0;
 +        rffac  = 1;
 +    }
 +    else
 +    {
 +        lambda = (eps0 - 1.0)/(2*epsRF - 1.0);
 +        rffac  = (2*epsRF+eps0)/(2*epsRF+1);
 +    }
 +    printf("DATA INTEGRAL: %5.1f, tauD(old) = %5.1f ps, "
 +           "tau_slope = %5.1f, tau_slope,D = %5.1f ps\n",
 +           integral, integral*rffac, fitparms[0], fitparms[0]*rffac);
 +
 +    printf("tau_D from tau1 = %8.3g , eps(Infty) = %8.3f\n",
 +           fitparms[0]*(1 + fitparms[1]*lambda),
 +           1 + ((1 - fitparms[1])*(eps0 - 1))/(1 + fitparms[1]*lambda));
 +
 +    fitintegral = numerical_deriv(nx, y[0], y[1], y[3], y[4], y[5], tendInt, nsmooth);
 +    printf("FIT INTEGRAL (tau_M): %5.1f, tau_D = %5.1f\n",
 +           fitintegral, fitintegral*rffac);
 +
 +    /* Now we have the negative gradient of <Phi(0) Phi(t)> */
 +    write_xvg(opt2fn("-d", NFILE, fnm), "Data", nx-1, 6, y, legend, oenv);
 +
 +    /* Do FFT and analysis */
 +    do_four(opt2fn("-o", NFILE, fnm), opt2fn("-c", NFILE, fnm),
 +            nx-1, y[0], y[5], eps0, epsRF, oenv);
 +
 +    do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
 +    do_view(oenv, opt2fn("-c", NFILE, fnm), NULL);
 +    do_view(oenv, opt2fn("-d", NFILE, fnm), "-nxy");
 +
 +    return 0;
 +}
index c06ce7836ed981202aa7caabb39e8f1a8618dfbd,0000000000000000000000000000000000000000..f2a91ab919f1ec2c1e474c36765ea5f90e60720f
mode 100644,000000..100644
--- /dev/null
@@@ -1,4425 -1,0 +1,4449 @@@
-     double                 *nrdf_tc, *nrdf_vcm, nrdf_uc, n_sub = 0;
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "readir.h"
 +
 +#include <ctype.h>
 +#include <limits.h>
 +#include <stdlib.h>
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fileio/readinp.h"
 +#include "gromacs/fileio/warninp.h"
 +#include "gromacs/gmxlib/chargegroup.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gmxpreprocess/toputil.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/units.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/calc_verletbuf.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/pull-params.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/topology/block.h"
 +#include "gromacs/topology/ifunc.h"
 +#include "gromacs/topology/index.h"
 +#include "gromacs/topology/mtop_util.h"
 +#include "gromacs/topology/symtab.h"
 +#include "gromacs/topology/topology.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/smalloc.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;
 +}
 +
 +
 +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 * std::pow(simtemp->simtemp_high/simtemp->simtemp_low, static_cast<real>((1.0*i)/(ntemps-1)));
 +        }
 +        else if (simtemp->eSimTempScale == esimtempEXPONENTIAL)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*(std::expm1(temperature_lambdas[i])/std::expm1(1.0));
 +        }
 +        else
 +        {
 +            char errorstr[128];
 +            sprintf(errorstr, "eSimTempScale=%d not defined", simtemp->eSimTempScale);
 +            gmx_fatal(FARGS, errorstr);
 +        }
 +    }
 +}
 +
 +
 +
 +static void _low_check(gmx_bool b, const 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 (EI_MD(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;
 +    real        dt_pcoupl;
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    if (ir->coulombtype == eelRF_NEC_UNSUPPORTED)
 +    {
 +        sprintf(warn_buf, "%s electrostatics is no longer supported",
 +                eel_names[eelRF_NEC_UNSUPPORTED]);
 +        warning_error(wi, warn_buf);
 +    }
 +
 +    /* 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");
 +    }
 +    sprintf(err_buf, "nstlist can not be smaller than 0. (If you were trying to use the heuristic neighbour-list update scheme for efficient buffering for improved energy conservation, please use the Verlet cut-off scheme instead.)");
 +    CHECK(ir->nstlist < 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 since 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.");
 +
 +        if (ir->rlist > 0 && ir->rlist < ir->rcoulomb)
 +        {
 +            gmx_fatal(FARGS, "rcoulomb must not be greater than rlist (twin-range schemes are not supported)");
 +        }
 +        if (ir->rlist > 0 && ir->rlist < ir->rvdw)
 +        {
 +            gmx_fatal(FARGS, "rvdw must not be greater than rlist (twin-range schemes are not supported)");
 +        }
 +
 +        if (ir->rlist == 0 && ir->ePBC != epbcNONE)
 +        {
 +            warning_error(wi, "Can not have an infinite cut-off with PBC");
 +        }
 +    }
 +
 +    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) ||
 +              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->implicit_solvent != eisNO)
 +        {
 +            warning_error(wi, "Implicit solvent is not (yet) supported with the with Verlet lists.");
 +        }
 +
 +        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 = std::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;
 +                }
 +            }
 +        }
 +    }
 +
 +    /* GENERAL INTEGRATOR STUFF */
 +    if (!EI_MD(ir->eI))
 +    {
 +        if (ir->etc != etcNO)
 +        {
 +            if (EI_RANDOM(ir->eI))
 +            {
 +                sprintf(warn_buf, "Setting tcoupl from '%s' to 'no'. %s handles temperature coupling implicitly. See the documentation for more information on which parameters affect temperature for %s.", etcoupl_names[ir->etc], ei_names[ir->eI], ei_names[ir->eI]);
 +            }
 +            else
 +            {
 +                sprintf(warn_buf, "Setting tcoupl from '%s' to 'no'. Temperature coupling does not apply to %s.", etcoupl_names[ir->etc], ei_names[ir->eI]);
 +            }
 +            warning_note(wi, warn_buf);
 +        }
 +        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))
 +    {
 +        if (ir->epc != epcNO)
 +        {
 +            sprintf(warn_buf, "Setting pcoupl from '%s' to 'no'. Pressure coupling does not apply to %s.", epcoupl_names[ir->epc], ei_names[ir->eI]);
 +            warning_note(wi, warn_buf);
 +        }
 +        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->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);
 +        }
 +    }
 +
 +    if (ir->nsteps == 0 && !ir->bContinuation)
 +    {
 +        warning_note(wi, "For a correct single-point energy evaluation with nsteps = 0, use continuation = yes to avoid constraining the input coordinates.");
 +    }
 +
 +    /* 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));
 +        sprintf(err_buf, "TPI does not work (yet) with the Verlet cut-off scheme");
 +        CHECK(ir->cutoff_scheme == ecutsVERLET);
 +    }
 +
 +    /* 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 positive 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 positive 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)");
 +        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   = std::pow(lambda*fep->sc_alpha*std::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;
 +
 +        /* 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, "nstlog must be non-zero");
 +            CHECK(ir->nstlog != 0);
 +            sprintf(err_buf, "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)",
 +                    expand->nstTij, ir->nstlog);
 +            CHECK((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_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 can lead to artifacts. Only use this on a single (cluster of) molecules. This cluster should not cross periodic boundaries.");
 +            }
 +        }
 +    }
 +
 +    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.");
 +    }
 +
 +    /* 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) - 10*GMX_REAL_EPS)
 +        {
 +            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 (ir->epsilon_r == 0)
 +    {
 +        sprintf(err_buf,
 +                "It is pointless to use long-range or Generalized Born electrostatics with infinite relative permittivity."
 +                "Since you are effectively turning of electrostatics, a plain cutoff will be much faster.");
 +        CHECK(EEL_FULL(ir->coulombtype) || ir->implicit_solvent == eisGBSA);
 +    }
 +
 +    if (getenv("GMX_DO_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 && ir->epsilon_rf != 0)
 +        {
 +            sprintf(warn_buf, "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0",
 +                    eel_names[ir->coulombtype]);
 +            warning(wi, warn_buf);
 +            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)
 +    {
 +        sprintf(err_buf,
 +                "Explicit switch/shift coulomb interactions cannot be used in combination with a secondary coulomb-modifier.");
 +        CHECK( ir->coulomb_modifier != eintmodNONE);
 +    }
 +    if (ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT)
 +    {
 +        sprintf(err_buf,
 +                "Explicit switch/shift vdw interactions cannot be used in combination with a secondary vdw-modifier.");
 +        CHECK( ir->vdw_modifier != eintmodNONE);
 +    }
 +
 +    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 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"
 +                        "For optimal energy conservation,consider using\n"
 +                        "a potential modifier.", eel_names[ir->coulombtype]);
 +                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->ewald_geometry == eewg3DC) && (ir->ePBC != epbcXY) &&
 +        EEL_FULL(ir->coulombtype))
 +    {
 +        sprintf(warn_buf, "With %s and ewald_geometry = %s you should use pbc = %s",
 +                eel_names[ir->coulombtype], eewg_names[eewg3DC], epbc_names[epbcXY]);
 +        warning(wi, warn_buf);
 +    }
 +    if ((ir->epsilon_surface != 0) && EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->cutoff_scheme == ecutsVERLET)
 +        {
 +            sprintf(warn_buf, "Since molecules/charge groups are broken using the Verlet scheme, you can not use a dipole correction to the %s electrostatics.",
 +                    eel_names[ir->coulombtype]);
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            sprintf(warn_buf, "Dipole corrections to %s electrostatics only work if all charge groups that can cross PBC boundaries are dipoles. If this is not the case set epsilon_surface to 0",
 +                    eel_names[ir->coulombtype]);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    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 and %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)))
 +        {
 +            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->rlist <= ir->rcoulomb)
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, rlist should be 0.1 to 0.3 nm larger than rcoulomb.");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir_vdw_switched(ir) && (ir->rlist <= ir->rvdw))
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, rlist should be 0.1 to 0.3 nm larger than rvdw.");
 +            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->eI == eiLBFGS && (ir->coulombtype == eelCUT || ir->vdwtype == evdwCUT)
 +        && ir->rvdw != 0)
 +    {
 +        warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off.");
 +    }
 +
 +    if (ir->eI == eiLBFGS && ir->nbfgscorr <= 0)
 +    {
 +        warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent.");
 +    }
 +
 +    /* ENERGY CONSERVATION */
 +    if (ir_NVE(ir) && ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (!ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > 0 && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for VdW interactions with NVE, for good energy conservation use vdwtype = %s (possibly with DispCorr)",
 +                    evdw_names[evdwSHIFT]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (!ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > 0)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for electrostatics with NVE, for good energy conservation use coulombtype = %s or %s",
 +                    eel_names[eelPMESWITCH], eel_names[eelRF_ZERO]);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    /* IMPLICIT SOLVENT */
 +    if (ir->coulombtype == eelGB_NOTUSED)
 +    {
 +        sprintf(warn_buf, "Invalid option %s for coulombtype",
 +                eel_names[ir->coulombtype]);
 +        warning_error(wi, warn_buf);
 +    }
 +
 +    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)
 +    {
 +        gmx_fatal(FARGS, "AdResS simulations are no longer supported");
 +    }
 +}
 +
 +/* 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 = gmx_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, warninp_t wi)
 +{
 +    char *ptr[MAXPTR];
 +    char *endptr;
 +    int   i;
 +    char  warn_buf[STRLEN];
 +
 +    *n = str_nelem(str, MAXPTR, ptr);
 +
 +    snew(*r, *n);
 +    for (i = 0; i < *n; i++)
 +    {
 +        (*r)[i] = strtod(ptr[i], &endptr);
 +        if (*endptr != 0)
 +        {
 +            sprintf(warn_buf, "Invalid value %s in string in mdp file. Expected a real number.", ptr[i]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN], char weights[STRLEN], warninp_t wi)
 +{
 +
 +    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]), wi);
 +    }
 +
 +    /* 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);
 +        }
 +    }
 +
 +    /* now read in the weights */
 +    parse_n_real(weights, &nweights, &(expand->init_lambda_weights), wi);
 +    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] = gmx_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;
 +    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;
 +}
 +
 +/*! \brief Return whether an end state with the given coupling-lambda
 + * value describes fully-interacting VDW.
 + *
 + * \param[in]  couple_lambda_value  Enumeration ecouplam value describing the end state
 + * \return                          Whether VDW is on (i.e. the user chose vdw or vdw-q in the .mdp file)
 + */
 +static gmx_bool couple_lambda_has_vdw_on(int couple_lambda_value)
 +{
 +    return (couple_lambda_value == ecouplamVDW ||
 +            couple_lambda_value == ecouplamVDWQ);
 +}
 +
 +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);
 +    }
 +
 +    /* ignore 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");
 +    REM_TYPE("optimize-fft");
 +    REM_TYPE("adress_type");
 +    REM_TYPE("adress_const_wf");
 +    REM_TYPE("adress_ex_width");
 +    REM_TYPE("adress_hy_width");
 +    REM_TYPE("adress_ex_forcecap");
 +    REM_TYPE("adress_interface_correction");
 +    REM_TYPE("adress_site");
 +    REM_TYPE("adress_reference_coords");
 +    REM_TYPE("adress_tf_grp_names");
 +    REM_TYPE("adress_cg_grp_names");
 +    REM_TYPE("adress_do_hybridpairs");
 +    REM_TYPE("rlistlong");
 +    REM_TYPE("nstcalclr");
 +    REM_TYPE("pull-print-com2");
 +
 +    /* 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");
 +    REPL_TYPE("pull-print-com1", "pull-print-com");
 +
 +    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);
 +    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);
 +    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");
 +
 +    /* 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);
 +
 +    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");
 +    EETYPE("pull",          ir->bPull, yesno_names);
 +    if (ir->bPull)
 +    {
 +        snew(ir->pull, 1);
 +        is->pull_grp = read_pullparams(&ninp, &inp, ir->pull, 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->edHdLPrintEnergy, edHdLPrintEnergy_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);
 +    CTYPE ("Time dependent (pulsed) electric field. Format is omega, time for pulse");
 +    CTYPE ("peak, and sigma (width) for pulse. Sigma = 0 removes pulse, leaving");
 +    CTYPE ("the field to be a cosine function.");
 +    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);
 +
 +    /* Ion/water position swapping ("computational electrophysiology") */
 +    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)
 +    {
 +        char buf[STRLEN];
 +        int  nIonTypes;
 +
 +
 +        snew(ir->swap, 1);
 +        CTYPE("Swap attempt frequency");
 +        ITYPE("swap-frequency", ir->swap->nstswap, 1);
 +        CTYPE("Number of ion types to be controlled");
 +        ITYPE("iontypes", nIonTypes, 1);
 +        if (nIonTypes < 1)
 +        {
 +            warning_error(wi, "You need to provide at least one ion type for position exchanges.");
 +        }
 +        ir->swap->ngrp = nIonTypes + eSwapFixedGrpNR;
 +        snew(ir->swap->grp, ir->swap->ngrp);
 +        for (i = 0; i < ir->swap->ngrp; i++)
 +        {
 +            snew(ir->swap->grp[i].molname, STRLEN);
 +        }
 +        CTYPE("Two index groups that contain the compartment-partitioning atoms");
 +        STYPE("split-group0", ir->swap->grp[eGrpSplit0].molname, NULL);
 +        STYPE("split-group1", ir->swap->grp[eGrpSplit1].molname, 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("Name of solvent molecules");
 +        STYPE("solvent-group", ir->swap->grp[eGrpSolvent].molname, 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 permeation events are recorded 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("Names of the ion types that can be exchanged with solvent molecules,");
 +        CTYPE("and the requested number of ions of this type in compartments A and B");
 +        CTYPE("-1 means fix the numbers as found in step 0");
 +        for (i = 0; i < nIonTypes; i++)
 +        {
 +            int ig = eSwapFixedGrpNR + i;
 +
 +            sprintf(buf, "iontype%d-name", i);
 +            STYPE(buf, ir->swap->grp[ig].molname, NULL);
 +            sprintf(buf, "iontype%d-in-A", i);
 +            ITYPE(buf, ir->swap->grp[ig].nmolReq[0], -1);
 +            sprintf(buf, "iontype%d-in-B", i);
 +            ITYPE(buf, ir->swap->grp[ig].nmolReq[1], -1);
 +        }
 +
 +        CTYPE("By default (i.e. bulk offset = 0.0), ion/water exchanges happen between layers");
 +        CTYPE("at maximum distance (= bulk concentration) to the split group layers. However,");
 +        CTYPE("an offset b (-1.0 < b < +1.0) can be specified to offset the bulk layer from the middle at 0.0");
 +        CTYPE("towards one of the compartment-partitioning layers (at +/- 1.0).");
 +        RTYPE("bulk-offsetA", ir->swap->bulkOffset[0], 0.0);
 +        RTYPE("bulk-offsetB", ir->swap->bulkOffset[1], 0.0);
 +        if (!(ir->swap->bulkOffset[0] > -1.0 && ir->swap->bulkOffset[0] < 1.0)
 +            || !(ir->swap->bulkOffset[1] > -1.0 && ir->swap->bulkOffset[1] < 1.0) )
 +        {
 +            warning_error(wi, "Bulk layer offsets must be > -1.0 and < 1.0 !");
 +        }
 +
 +        CTYPE("Start to swap ions if threshold difference to requested count is reached");
 +        RTYPE("threshold", ir->swap->threshold, 1.0);
 +    }
 +
 +    /* AdResS is no longer supported, but we need mdrun to be able to refuse to run old AdResS .tpr files */
 +    EETYPE("adress", ir->bAdress, yesno_names);
 +
 +    /* 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 = gmx_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_note(wi, "Free energy is turned off, so we will not decouple the molecule listed in your input.");
 +        }
 +    }
 +    /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
 +    if (ir->efep != efepNO)
 +    {
 +        if (fep->delta_lambda > 0)
 +        {
 +            ir->efep = efepSLOWGROWTH;
 +        }
 +    }
 +
 +    if (fep->edHdLPrintEnergy == edHdLPrintEnergyYES)
 +    {
 +        fep->edHdLPrintEnergy = edHdLPrintEnergyTOTAL;
 +        warning_note(wi, "Old option for dhdl-print-energy given: "
 +                     "changing \"yes\" to \"total\"\n");
 +    }
 +
 +    if (ir->bSimTemp && (fep->edHdLPrintEnergy == edHdLPrintEnergyNO))
 +    {
 +        /* always print out the energy to dhdl if we are doing
 +           expanded ensemble, since we need the total energy for
 +           analysis if the temperature is changing. In some
 +           conditions one may only want the potential energy, so
 +           we will allow that if the appropriate mdp setting has
 +           been enabled. Otherwise, total it is:
 +         */
 +        fep->edHdLPrintEnergy = edHdLPrintEnergyTOTAL;
 +    }
 +
 +    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, wi);
 +        if (ir->bSimTemp) /* done after fep params */
 +        {
 +            do_simtemp_params(ir);
 +        }
 +
 +        /* Because sc-coul (=FALSE by default) only acts on the lambda state
 +         * setup and not on the old way of specifying the free-energy setup,
 +         * we should check for using soft-core when not needed, since that
 +         * can complicate the sampling significantly.
 +         * Note that we only check for the automated coupling setup.
 +         * If the (advanced) user does FEP through manual topology changes,
 +         * this check will not be triggered.
 +         */
 +        if (ir->efep != efepNO && ir->fepvals->n_lambda == 0 &&
 +            ir->fepvals->sc_alpha != 0 &&
 +            (couple_lambda_has_vdw_on(opts->couple_lam0) &&
 +             couple_lambda_has_vdw_on(opts->couple_lam1)))
 +        {
 +            warning(wi, "You are using soft-core interactions while the Van der Waals interactions are not decoupled (note that the sc-coul option is only active when using lambda states). Although this will not lead to errors, you will need much more sampling than without soft-core interactions. Consider using sc-alpha=0.");
 +        }
 +    }
 +    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;
 +    }
 +    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 */
 +/* TODO this is utility functionality (search for the index of a
 +   string in a collection), so should be refactored and located more
 +   centrally. */
 +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;
 +    pull_params_t          *pull;
 +    int                     natoms, ai, aj, i, j, d, g, imin, jmin;
 +    t_iatom                *ia;
 +    int                    *nrdf2, *na_vcm, na_tot;
-         nrdf_vcm[i] = 0;
++    double                 *nrdf_tc, *nrdf_vcm, nrdf_uc, *nrdf_vcm_sub;
++    ivec                   *dof_vcm;
 +    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(dof_vcm, groups->grps[egcVCM].nr+1);
 +    snew(na_vcm, groups->grps[egcVCM].nr+1);
++    snew(nrdf_vcm_sub, 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++)
 +    {
-             /* Double count nrdf for particle i */
++        nrdf_vcm[i]     = 0;
++        clear_ivec(dof_vcm[i]);
++        na_vcm[i]       = 0;
++        nrdf_vcm_sub[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);
-                     nrdf2[i] += 2;
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (opts->nFreeze[g][d] == 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.
++                    /* Add one DOF for particle i (counted as 2*1) */
++                    nrdf2[i]                              += 2;
++                    /* VCM group i has dim d as a DOF */
++                    dof_vcm[ggrpnr(groups, egcVCM, i)][d]  = 1;
 +                }
 +            }
 +            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       = std::min(imin, nrdf2[ai]);
 +                        jmin       = std::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       = std::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->bPull)
 +    {
 +        /* 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++)
 +        {
 +            if (pull->coord[i].eType != epullCONSTRAINT)
 +            {
 +                continue;
 +            }
 +
 +            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)
 +    {
-         switch (ir->comm_mode)
++        int ndim_rm_vcm;
++
++        /* We remove COM motion up to dim ndof_com() */
++        ndim_rm_vcm = ndof_com(ir);
++
++        /* Subtract ndim_rm_vcm (or less with frozen dimensions) from
++         * the number of degrees of freedom in each vcm group when COM
++         * translation is removed and 6 when rotation is removed as well.
 +         */
-             case ecmLINEAR:
-                 n_sub = ndof_com(ir);
-                 break;
-             case ecmANGULAR:
-                 n_sub = 6;
-                 break;
-             default:
-                 gmx_incons("Checking comm_mode");
++        for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +        {
-                 fprintf(debug, "T-group[%d] nrdf_uc = %g, n_sub = %g\n",
-                         i, nrdf_uc, n_sub);
++            switch (ir->comm_mode)
++            {
++                case ecmLINEAR:
++                    nrdf_vcm_sub[j] = 0;
++                    for (d = 0; d < ndim_rm_vcm; d++)
++                    {
++                        if (dof_vcm[j][d])
++                        {
++                            nrdf_vcm_sub[j]++;
++                        }
++                    }
++                    break;
++                case ecmANGULAR:
++                    nrdf_vcm_sub[j] = 6;
++                    break;
++                default:
++                    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)
 +            {
-                 if (nrdf_vcm[j] > n_sub)
++                fprintf(debug, "T-group[%d] nrdf_uc = %g\n", i, nrdf_uc);
 +            }
 +            nrdf_tc[i] = 0;
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
-                         (nrdf_vcm[j] - n_sub)/nrdf_vcm[j];
++                if (nrdf_vcm[j] > nrdf_vcm_sub[j])
 +                {
 +                    nrdf_tc[i] += nrdf_uc*((double)na_vcm[j]/(double)na_tot)*
++                        (nrdf_vcm[j] - nrdf_vcm_sub[j])/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(dof_vcm);
 +    sfree(na_vcm);
++    sfree(nrdf_vcm_sub);
 +}
 +
 +static void decode_cos(char *s, t_cosines *cosine)
 +{
 +    char   *t;
 +    char    format[STRLEN], f1[STRLEN];
 +    double  a, phi;
 +    int     i;
 +
 +    t = gmx_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,
 +        t_blocka      *grps,
 +        char         **gnames)
 +{
 +    int          ig = -1, i = 0, gind;
 +    t_swapGroup *swapg;
 +
 +
 +    /* Just a quick check here, more thorough checks are in mdrun */
 +    if (strcmp(swap->grp[eGrpSplit0].molname, swap->grp[eGrpSplit1].molname) == 0)
 +    {
 +        gmx_fatal(FARGS, "The split groups can not both be '%s'.", swap->grp[eGrpSplit0].molname);
 +    }
 +
 +    /* Get the index atoms of the split0, split1, solvent, and swap groups */
 +    for (ig = 0; ig < swap->ngrp; ig++)
 +    {
 +        swapg      = &swap->grp[ig];
 +        gind       = search_string(swap->grp[ig].molname, grps->nr, gnames);
 +        swapg->nat = grps->index[gind+1] - grps->index[gind];
 +
 +        if (swapg->nat > 0)
 +        {
 +            fprintf(stderr, "%s group '%s' contains %d atoms.\n",
 +                    ig < 3 ? eSwapFixedGrp_names[ig] : "Swap",
 +                    swap->grp[ig].molname, swapg->nat);
 +            snew(swapg->ind, swapg->nat);
 +            for (i = 0; i < swapg->nat; i++)
 +            {
 +                swapg->ind[i] = grps->a[grps->index[gind]+i];
 +            }
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Swap group %s does not contain any atoms.", swap->grp[ig].molname);
 +        }
 +    }
 +}
 +
 +
 +void make_IMD_group(t_IMD *IMDgroup, char *IMDgname, t_blocka *grps, char **gnames)
 +{
 +    int      ig, 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,
 +              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;
 +    gmx_bool      bExcl, bTable, bSetTCpar, bAnneal, bRest;
 +    int           nQMmethod, nQMbasis, nQMg;
 +    char          warn_buf[STRLEN];
 +    char*         endptr;
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing index file...\n");
 +    }
 +    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);
 +        done_atom(&atoms_all);
 +    }
 +    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], &endptr);
 +            if (*endptr != 0)
 +            {
 +                warning_error(wi, "Invalid value for mdp option tau-t. tau-t should only consist of real numbers separated by spaces.");
 +            }
 +            if ((ir->eI == eiBD) && 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 = std::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)
 +            {
 +                if (ir->etc != etcNOSEHOOVER)
 +                {
 +                    gmx_fatal(FARGS, "Cannot do MTTK pressure coupling without Nose-Hoover temperature control");
 +                }
 +                else
 +                {
 +                    if (ir->nstpcouple != ir->nsttcouple)
 +                    {
 +                        int mincouple = std::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 - 10*GMX_REAL_EPS)
 +            {
 +                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], &endptr);
 +            if (*endptr != 0)
 +            {
 +                warning_error(wi, "Invalid value for mdp option ref-t. ref-t should only consist of real numbers separated by spaces.");
 +            }
 +            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], &endptr, 10);
 +                    if (*endptr != 0)
 +                    {
 +                        warning_error(wi, "Invalid value for mdp option annealing-npoints. annealing should only consist of integers separated by spaces.");
 +                    }
 +                    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], &endptr);
 +                        if (*endptr != 0)
 +                        {
 +                            warning_error(wi, "Invalid value for mdp option anneal-time. anneal-time should only consist of real numbers separated by spaces.");
 +                        }
 +                        ir->opts.anneal_temp[i][j] = strtod(ptr2[k], &endptr);
 +                        if (*endptr != 0)
 +                        {
 +                            warning_error(wi, "Invalid value for anneal-temp. anneal-temp should only consist of real numbers separated by spaces.");
 +                        }
 +                        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->bPull)
 +    {
 +        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, 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], &endptr);
 +            if (*endptr != 0)
 +            {
 +                warning_error(wi, "Invalid value for mdp option accelerate. accelerate should only consist of real numbers separated by spaces.");
 +            }
 +        }
 +    }
 +    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);
 +
 +    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);
 +
 +    }
 +    str_nelem(is->QMmult, MAXPTR, ptr1);
 +    str_nelem(is->QMcharge, MAXPTR, ptr2);
 +    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], &endptr, 10);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option QMmult. QMmult should only consist of integers separated by spaces.");
 +        }
 +        ir->opts.QMcharge[i] = strtol(ptr2[i], &endptr, 10);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option QMcharge. QMcharge should only consist of integers separated by spaces.");
 +        }
 +        ir->opts.bSH[i]      = (gmx_strncasecmp(ptr3[i], "Y", 1) == 0);
 +    }
 +
 +    str_nelem(is->CASelectrons, MAXPTR, ptr1);
 +    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], &endptr, 10);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option CASelectrons. CASelectrons should only consist of integers separated by spaces.");
 +        }
 +        ir->opts.CASorbitals[i]  = strtol(ptr2[i], &endptr, 10);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option CASorbitals. CASorbitals should only consist of integers separated by spaces.");
 +        }
 +    }
 +    /* special optimization options */
 +
 +    str_nelem(is->bOPT, MAXPTR, ptr1);
 +    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);
 +    }
 +    str_nelem(is->SAon, MAXPTR, ptr1);
 +    str_nelem(is->SAoff, MAXPTR, ptr2);
 +    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], &endptr);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option SAon. SAon should only consist of real numbers separated by spaces.");
 +        }
 +        ir->opts.SAoff[i]   = strtod(ptr2[i], &endptr);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option SAoff. SAoff should only consist of real numbers separated by spaces.");
 +        }
 +        ir->opts.SAsteps[i] = strtol(ptr3[i], &endptr, 10);
 +        if (*endptr != 0)
 +        {
 +            warning_error(wi, "Invalid value for mdp option SAsteps. SAsteps should only consist of integers separated by spaces.");
 +        }
 +    }
 +    /* 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]));
 +
 +    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 efbposresCYLINDERX:
 +                            AbsRef[YY] = AbsRef[ZZ] = 1;
 +                            break;
 +                        case efbposresCYLINDERY:
 +                            AbsRef[XX] = AbsRef[ZZ] = 1;
 +                            break;
 +                        case efbposresCYLINDER:
 +                        /* efbposres is a synonym for efbposresCYLINDERZ for backwards compatibility */
 +                        case efbposresCYLINDERZ:
 +                            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;
 +    int          *typecount;
 +    real          tol;
 +    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;
 +    ntypes                                = mtop->ffparams.atnr;
 +    snew(typecount, ntypes);
 +    gmx_mtop_count_atomtypes(mtop, state, typecount);
 +    *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 = std::sqrt(c6i * c6j);
 +            if (!gmx_numzero(c6_geometric))
 +            {
 +                if (!gmx_numzero(c12i) && !gmx_numzero(c12j))
 +                {
 +                    sigmai   = gmx::sixthroot(c12i / c6i);
 +                    sigmaj   = gmx::sixthroot(c12j / c6j);
 +                    epsi     = c6i * c6i /(4.0 * c12i);
 +                    epsj     = c6j * c6j /(4.0 * c12j);
 +                    c6_LB    = 4.0 * std::sqrt(epsi * epsj) * gmx::power6(0.5 * (sigmai + sigmaj));
 +                }
 +                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)
 +{
 +    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;
 +    gmx_bool                  bCharge, bAcc;
 +    real                     *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   = std::max(T, ir->opts.ref_t[i]);
 +            tau = std::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 positive 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));
 +    }
 +
 +    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->bPull)
 +    {
 +        gmx_bool bWarned;
 +
 +        bWarned = FALSE;
 +        for (i = 0; i < ir->pull->ncoord && !bWarned; i++)
 +        {
 +            if (ir->pull->coord[i].group[0] == 0 ||
 +                ir->pull->coord[i].group[1] == 0)
 +            {
 +                absolute_reference(ir, sys, FALSE, AbsRef);
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (ir->pull->coord[i].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.");
 +                        bWarned = TRUE;
 +                        break;
 +                    }
 +                }
 +            }
 +        }
 +
 +        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].eGeom == epullgDIRPBC &&
 +                            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->coord[c].eGeom), 'x'+m);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    check_disre(sys);
 +}
 +
 +void double_check(t_inputrec *ir, matrix box,
 +                  gmx_bool bHasNormalConstraints,
 +                  gmx_bool bHasAnyConstraints,
 +                  warninp_t wi)
 +{
 +    real        min_size;
 +    char        warn_buf[STRLEN];
 +    const char *ptr;
 +
 +    ptr = check_box(ir->ePBC, box);
 +    if (ptr)
 +    {
 +        warning_error(wi, ptr);
 +    }
 +
 +    if (bHasNormalConstraints && 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->eConstrAlg == econtLINCS) && bHasNormalConstraints)
 +    {
 +        /* 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 (bHasAnyConstraints && ir->epc == epcMTTK)
 +    {
 +        warning_error(wi, "Constraints are not implemented with MTTK pressure control.");
 +    }
 +
 +    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.");
 +        }
 +        if (ir->ns_type == ensGRID)
 +        {
 +            if (gmx::square(ir->rlist) >= 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 rlist.\n");
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +        else
 +        {
 +            min_size = std::min(box[XX][XX], std::min(box[YY][YY], box[ZZ][ZZ]));
 +            if (2*ir->rlist >= 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",
 +                    std::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->rlist - ir->rvdw)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group "
 +                        "radii (%f) is larger than rlist (%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->rlist, 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->rlist - ir->rcoulomb)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than rlist (%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->rlist, ir->rcoulomb,
 +                        ecutscheme_names[ecutsVERLET]);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +}
index 9bc3a53aecc10af74ad4615915f824cf682d81dc,0000000000000000000000000000000000000000..3763c2e55a7319e94931b14046c164f97a992a19
mode 100644,000000..100644
--- /dev/null
@@@ -1,2741 -1,0 +1,2766 @@@
-     int          i, j, nparam_found;
 +/*
 + * 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,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "toppush.h"
 +
 +#include <ctype.h>
 +#include <stdlib.h>
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fileio/warninp.h"
 +#include "gromacs/gmxpreprocess/gpp_atomtype.h"
 +#include "gromacs/gmxpreprocess/gpp_bond_atomtype.h"
 +#include "gromacs/gmxpreprocess/notset.h"
 +#include "gromacs/gmxpreprocess/readir.h"
 +#include "gromacs/gmxpreprocess/topdirs.h"
 +#include "gromacs/gmxpreprocess/toputil.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/topology/ifunc.h"
 +#include "gromacs/topology/symtab.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/gmxassert.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +void generate_nbparams(int comb, int ftype, t_params *plist, gpp_atomtype_t atype,
 +                       warninp_t wi)
 +{
 +    int   i, j, k = -1, nf;
 +    int   nr, nrfp;
 +    real  c, bi, bj, ci, cj, ci0, ci1, ci2, cj0, cj1, cj2;
 +    char  errbuf[256];
 +
 +    /* Lean mean shortcuts */
 +    nr   = get_atomtype_ntypes(atype);
 +    nrfp = NRFP(ftype);
 +    snew(plist->param, nr*nr);
 +    plist->nr = nr*nr;
 +
 +    /* Fill the matrix with force parameters */
 +    switch (ftype)
 +    {
 +        case F_LJ:
 +            switch (comb)
 +            {
 +                case eCOMB_GEOMETRIC:
 +                    /* Gromos rules */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            for (nf = 0; (nf < nrfp); nf++)
 +                            {
 +                                ci = get_atomtype_nbparam(i, nf, atype);
 +                                cj = get_atomtype_nbparam(j, nf, atype);
 +                                c  = std::sqrt(ci * cj);
 +                                plist->param[k].c[nf]      = c;
 +                            }
 +                        }
 +                    }
 +                    break;
 +
 +                case eCOMB_ARITHMETIC:
 +                    /* c0 and c1 are sigma and epsilon */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                            cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                            ci1                  = get_atomtype_nbparam(i, 1, atype);
 +                            cj1                  = get_atomtype_nbparam(j, 1, atype);
 +                            plist->param[k].c[0] = (fabs(ci0) + fabs(cj0))*0.5;
 +                            /* Negative sigma signals that c6 should be set to zero later,
 +                             * so we need to propagate that through the combination rules.
 +                             */
 +                            if (ci0 < 0 || cj0 < 0)
 +                            {
 +                                plist->param[k].c[0] *= -1;
 +                            }
 +                            plist->param[k].c[1] = std::sqrt(ci1*cj1);
 +                        }
 +                    }
 +
 +                    break;
 +                case eCOMB_GEOM_SIG_EPS:
 +                    /* c0 and c1 are sigma and epsilon */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                            cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                            ci1                  = get_atomtype_nbparam(i, 1, atype);
 +                            cj1                  = get_atomtype_nbparam(j, 1, atype);
 +                            plist->param[k].c[0] = std::sqrt(fabs(ci0*cj0));
 +                            /* Negative sigma signals that c6 should be set to zero later,
 +                             * so we need to propagate that through the combination rules.
 +                             */
 +                            if (ci0 < 0 || cj0 < 0)
 +                            {
 +                                plist->param[k].c[0] *= -1;
 +                            }
 +                            plist->param[k].c[1] = std::sqrt(ci1*cj1);
 +                        }
 +                    }
 +
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "No such combination rule %d", comb);
 +            }
 +            if (plist->nr != k)
 +            {
 +                gmx_incons("Topology processing, generate nb parameters");
 +            }
 +            break;
 +
 +        case F_BHAM:
 +            /* Buckingham rules */
 +            for (i = k = 0; (i < nr); i++)
 +            {
 +                for (j = 0; (j < nr); j++, k++)
 +                {
 +                    ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                    cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                    ci2                  = get_atomtype_nbparam(i, 2, atype);
 +                    cj2                  = get_atomtype_nbparam(j, 2, atype);
 +                    bi                   = get_atomtype_nbparam(i, 1, atype);
 +                    bj                   = get_atomtype_nbparam(j, 1, atype);
 +                    plist->param[k].c[0] = std::sqrt(ci0 * cj0);
 +                    if ((bi == 0) || (bj == 0))
 +                    {
 +                        plist->param[k].c[1] = 0;
 +                    }
 +                    else
 +                    {
 +                        plist->param[k].c[1] = 2.0/(1/bi+1/bj);
 +                    }
 +                    plist->param[k].c[2] = std::sqrt(ci2 * cj2);
 +                }
 +            }
 +
 +            break;
 +        default:
 +            sprintf(errbuf, "Invalid nonbonded type %s",
 +                    interaction_function[ftype].longname);
 +            warning_error(wi, errbuf);
 +    }
 +}
 +
 +static void realloc_nb_params(gpp_atomtype_t at,
 +                              t_nbparam ***nbparam, t_nbparam ***pair)
 +{
 +    /* Add space in the non-bonded parameters matrix */
 +    int atnr = get_atomtype_ntypes(at);
 +    srenew(*nbparam, atnr);
 +    snew((*nbparam)[atnr-1], atnr);
 +    if (pair)
 +    {
 +        srenew(*pair, atnr);
 +        snew((*pair)[atnr-1], atnr);
 +    }
 +}
 +
 +static void copy_B_from_A(int ftype, double *c)
 +{
 +    int nrfpA, nrfpB, i;
 +
 +    nrfpA = NRFPA(ftype);
 +    nrfpB = NRFPB(ftype);
 +
 +    /* Copy the B parameters from the first nrfpB A parameters */
 +    for (i = 0; (i < nrfpB); i++)
 +    {
 +        c[nrfpA+i] = c[i];
 +    }
 +}
 +
 +void push_at (t_symtab *symtab, gpp_atomtype_t at, t_bond_atomtype bat,
 +              char *line, int nb_funct,
 +              t_nbparam ***nbparam, t_nbparam ***pair,
 +              warninp_t wi)
 +{
 +    typedef struct {
 +        const char *entry;
 +        int         ptype;
 +    } t_xlate;
 +    t_xlate    xl[eptNR] = {
 +        { "A",   eptAtom },
 +        { "N",   eptNucleus },
 +        { "S",   eptShell },
 +        { "B",   eptBond },
 +        { "V",   eptVSite },
 +    };
 +
 +    int        nr, i, nfields, j, pt, nfp0 = -1;
 +    int        batype_nr, nread;
 +    char       type[STRLEN], btype[STRLEN], ptype[STRLEN];
 +    double     m, q;
 +    double     c[MAXFORCEPARAM];
 +    double     radius, vol, surftens, gb_radius, S_hct;
 +    char       tmpfield[12][100]; /* Max 12 fields of width 100 */
 +    char       errbuf[256];
 +    t_atom    *atom;
 +    t_param   *param;
 +    int        atomnr;
 +    gmx_bool   have_atomic_number;
 +    gmx_bool   have_bonded_type;
 +
 +    snew(atom, 1);
 +    snew(param, 1);
 +
 +    /* First assign input line to temporary array */
 +    nfields = sscanf(line, "%s%s%s%s%s%s%s%s%s%s%s%s",
 +                     tmpfield[0], tmpfield[1], tmpfield[2], tmpfield[3], tmpfield[4], tmpfield[5],
 +                     tmpfield[6], tmpfield[7], tmpfield[8], tmpfield[9], tmpfield[10], tmpfield[11]);
 +
 +    /* Comments on optional fields in the atomtypes section:
 +     *
 +     * The force field format is getting a bit old. For OPLS-AA we needed
 +     * to add a special bonded atomtype, and for Gerrit Groenhofs QM/MM stuff
 +     * we also needed the atomic numbers.
 +     * To avoid making all old or user-generated force fields unusable we
 +     * have introduced both these quantities as optional columns, and do some
 +     * acrobatics to check whether they are present or not.
 +     * This will all look much nicer when we switch to XML... sigh.
 +     *
 +     * Field 0 (mandatory) is the nonbonded type name. (string)
 +     * Field 1 (optional)  is the bonded type (string)
 +     * Field 2 (optional)  is the atomic number (int)
 +     * Field 3 (mandatory) is the mass (numerical)
 +     * Field 4 (mandatory) is the charge (numerical)
 +     * Field 5 (mandatory) is the particle type (single character)
 +     * This is followed by a number of nonbonded parameters.
 +     *
 +     * The safest way to identify the format is the particle type field.
 +     *
 +     * So, here is what we do:
 +     *
 +     * A. Read in the first six fields as strings
 +     * B. If field 3 (starting from 0) is a single char, we have neither
 +     *    bonded_type or atomic numbers.
 +     * C. If field 5 is a single char we have both.
 +     * D. If field 4 is a single char we check field 1. If this begins with
 +     *    an alphabetical character we have bonded types, otherwise atomic numbers.
 +     */
 +
 +    if (nfields < 6)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    if ( (strlen(tmpfield[5]) == 1) && isalpha(tmpfield[5][0]) )
 +    {
 +        have_bonded_type   = TRUE;
 +        have_atomic_number = TRUE;
 +    }
 +    else if ( (strlen(tmpfield[3]) == 1) && isalpha(tmpfield[3][0]) )
 +    {
 +        have_bonded_type   = FALSE;
 +        have_atomic_number = FALSE;
 +    }
 +    else
 +    {
 +        have_bonded_type   = ( isalpha(tmpfield[1][0]) != 0 );
 +        have_atomic_number = !have_bonded_type;
 +    }
 +
 +    /* optional fields */
 +    surftens  = -1;
 +    vol       = -1;
 +    radius    = -1;
 +    gb_radius = -1;
 +    atomnr    = -1;
 +    S_hct     = -1;
 +
 +    switch (nb_funct)
 +    {
 +
 +        case F_LJ:
 +            nfp0 = 2;
 +
 +            if (have_atomic_number)
 +            {
 +                if (have_bonded_type)
 +                {
 +                    nread = sscanf(line, "%s%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &atomnr, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, &atomnr, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (have_bonded_type)
 +                {
 +                    /* !have_atomic_number && have_bonded_type */
 +                    nread = sscanf(line, "%s%s%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* !have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 6)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if (!have_bonded_type)
 +            {
 +                strcpy(btype, type);
 +            }
 +
 +            if (!have_atomic_number)
 +            {
 +                atomnr = -1;
 +            }
 +
 +            break;
 +
 +        case F_BHAM:
 +            nfp0 = 3;
 +
 +            if (have_atomic_number)
 +            {
 +                if (have_bonded_type)
 +                {
 +                    nread = sscanf(line, "%s%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &atomnr, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 9)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, &atomnr, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (have_bonded_type)
 +                {
 +                    /* !have_atomic_number && have_bonded_type */
 +                    nread = sscanf(line, "%s%s%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* !have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if (!have_bonded_type)
 +            {
 +                strcpy(btype, type);
 +            }
 +
 +            if (!have_atomic_number)
 +            {
 +                atomnr = -1;
 +            }
 +
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Invalid function type %d in push_at %s %d", nb_funct,
 +                      __FILE__, __LINE__);
 +    }
 +    for (j = nfp0; (j < MAXFORCEPARAM); j++)
 +    {
 +        c[j] = 0.0;
 +    }
 +
 +    if (strlen(type) == 1 && isdigit(type[0]))
 +    {
 +        gmx_fatal(FARGS, "Atom type names can't be single digits.");
 +    }
 +
 +    if (strlen(btype) == 1 && isdigit(btype[0]))
 +    {
 +        gmx_fatal(FARGS, "Bond atom type names can't be single digits.");
 +    }
 +
 +    /* Hack to read old topologies */
 +    if (gmx_strcasecmp(ptype, "D") == 0)
 +    {
 +        sprintf(ptype, "V");
 +    }
 +    for (j = 0; (j < eptNR); j++)
 +    {
 +        if (gmx_strcasecmp(ptype, xl[j].entry) == 0)
 +        {
 +            break;
 +        }
 +    }
 +    if (j == eptNR)
 +    {
 +        gmx_fatal(FARGS, "Invalid particle type %s on line %s",
 +                  ptype, line);
 +    }
 +    pt = xl[j].ptype;
 +    if (debug)
 +    {
 +        fprintf(debug, "ptype: %s\n", ptype_str[pt]);
 +    }
 +
 +    atom->q     = q;
 +    atom->m     = m;
 +    atom->ptype = pt;
 +    for (i = 0; (i < MAXFORCEPARAM); i++)
 +    {
 +        param->c[i] = c[i];
 +    }
 +
 +    if ((batype_nr = get_bond_atomtype_type(btype, bat)) == NOTSET)
 +    {
 +        add_bond_atomtype(bat, symtab, btype);
 +    }
 +    batype_nr = get_bond_atomtype_type(btype, bat);
 +
 +    if ((nr = get_atomtype_type(type, at)) != NOTSET)
 +    {
 +        sprintf(errbuf, "Overriding atomtype %s", type);
 +        warning(wi, errbuf);
 +        if ((nr = set_atomtype(nr, at, symtab, atom, type, param, batype_nr,
 +                               radius, vol, surftens, atomnr, gb_radius, S_hct)) == NOTSET)
 +        {
 +            gmx_fatal(FARGS, "Replacing atomtype %s failed", type);
 +        }
 +    }
 +    else if ((add_atomtype(at, symtab, atom, type, param,
 +                           batype_nr, radius, vol,
 +                           surftens, atomnr, gb_radius, S_hct)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Adding atomtype %s failed", type);
 +    }
 +    else
 +    {
 +        /* Add space in the non-bonded parameters matrix */
 +        realloc_nb_params(at, nbparam, pair);
 +    }
 +    sfree(atom);
 +    sfree(param);
 +}
 +
 +static void push_bondtype(t_params     *       bt,
 +                          t_param     *        b,
 +                          int                  nral,
 +                          int                  ftype,
 +                          gmx_bool             bAllowRepeat,
 +                          char     *           line,
 +                          warninp_t            wi)
 +{
 +    int      i, j;
 +    gmx_bool bTest, bFound, bCont, bId;
 +    int      nr   = bt->nr;
 +    int      nrfp = NRFP(ftype);
 +    char     errbuf[256];
 +
 +    /* If bAllowRepeat is TRUE, we allow multiple entries as long as they
 +       are on directly _adjacent_ lines.
 +     */
 +
 +    /* First check if our atomtypes are _identical_ (not reversed) to the previous
 +       entry. If they are not identical we search for earlier duplicates. If they are
 +       we can skip it, since we already searched for the first line
 +       in this group.
 +     */
 +
 +    bFound = FALSE;
 +    bCont  = FALSE;
 +
 +    if (bAllowRepeat && nr > 1)
 +    {
 +        for (j = 0, bCont = TRUE; (j < nral); j++)
 +        {
 +            bCont = bCont && (b->a[j] == bt->param[nr-2].a[j]);
 +        }
 +    }
 +
 +    /* Search for earlier duplicates if this entry was not a continuation
 +       from the previous line.
 +     */
 +    if (!bCont)
 +    {
 +        bFound = FALSE;
 +        for (i = 0; (i < nr); i++)
 +        {
 +            bTest = TRUE;
 +            for (j = 0; (j < nral); j++)
 +            {
 +                bTest = (bTest && (b->a[j] == bt->param[i].a[j]));
 +            }
 +            if (!bTest)
 +            {
 +                bTest = TRUE;
 +                for (j = 0; (j < nral); j++)
 +                {
 +                    bTest = (bTest && (b->a[nral-1-j] == bt->param[i].a[j]));
 +                }
 +            }
 +            if (bTest)
 +            {
 +                if (!bFound)
 +                {
 +                    bId = TRUE;
 +                    for (j = 0; (j < nrfp); j++)
 +                    {
 +                        bId = bId && (bt->param[i].c[j] == b->c[j]);
 +                    }
 +                    if (!bId)
 +                    {
 +                        sprintf(errbuf, "Overriding %s parameters.%s",
 +                                interaction_function[ftype].longname,
 +                                (ftype == F_PDIHS) ?
 +                                "\nUse dihedraltype 9 to allow several multiplicity terms. Only consecutive lines are combined. Non-consective lines overwrite each other."
 +                                : "");
 +                        warning(wi, errbuf);
 +                        fprintf(stderr, "  old:                                         ");
 +                        for (j = 0; (j < nrfp); j++)
 +                        {
 +                            fprintf(stderr, " %g", bt->param[i].c[j]);
 +                        }
 +                        fprintf(stderr, " \n  new: %s\n\n", line);
 +                    }
 +                }
 +                /* Overwrite it! */
 +                for (j = 0; (j < nrfp); j++)
 +                {
 +                    bt->param[i].c[j] = b->c[j];
 +                }
 +                bFound = TRUE;
 +            }
 +        }
 +    }
 +    if (!bFound)
 +    {
 +        /* alloc */
 +        pr_alloc (2, bt);
 +
 +        /* fill the arrays up and down */
 +        memcpy(bt->param[bt->nr].c,  b->c, sizeof(b->c));
 +        memcpy(bt->param[bt->nr].a,  b->a, sizeof(b->a));
 +        memcpy(bt->param[bt->nr+1].c, b->c, sizeof(b->c));
 +
 +        /* The definitions of linear angles depend on the order of atoms,
 +         * that means that for atoms i-j-k, with certain parameter a, the
 +         * corresponding k-j-i angle will have parameter 1-a.
 +         */
 +        if (ftype == F_LINEAR_ANGLES)
 +        {
 +            bt->param[bt->nr+1].c[0] = 1-bt->param[bt->nr+1].c[0];
 +            bt->param[bt->nr+1].c[2] = 1-bt->param[bt->nr+1].c[2];
 +        }
 +
 +        for (j = 0; (j < nral); j++)
 +        {
 +            bt->param[bt->nr+1].a[j] = b->a[nral-1-j];
 +        }
 +
 +        bt->nr += 2;
 +    }
 +}
 +
 +void push_bt(directive d, t_params bt[], int nral,
 +             gpp_atomtype_t at,
 +             t_bond_atomtype bat, char *line,
 +             warninp_t wi)
 +{
 +    const char *formal[MAXATOMLIST+1] = {
 +        "%s",
 +        "%s%s",
 +        "%s%s%s",
 +        "%s%s%s%s",
 +        "%s%s%s%s%s",
 +        "%s%s%s%s%s%s",
 +        "%s%s%s%s%s%s%s"
 +    };
 +    const char *formnl[MAXATOMLIST+1] = {
 +        "%*s",
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char *formlf = "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf";
 +    int         i, ft, ftype, nn, nrfp, nrfpA;
 +    char        f1[STRLEN];
 +    char        alc[MAXATOMLIST+1][20];
 +    /* One force parameter more, so we can check if we read too many */
 +    double      c[MAXFORCEPARAM+1];
 +    t_param     p;
 +    char        errbuf[256];
 +
 +    if ((bat && at) || (!bat && !at))
 +    {
 +        gmx_incons("You should pass either bat or at to push_bt");
 +    }
 +
 +    /* Make format string (nral ints+functype) */
 +    if ((nn = sscanf(line, formal[nral],
 +                     alc[0], alc[1], alc[2], alc[3], alc[4], alc[5])) != nral+1)
 +    {
 +        sprintf(errbuf, "Not enough atomtypes (%d instead of %d)", nn-1, nral);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    ft    = strtol(alc[nral], NULL, 10);
 +    ftype = ifunc_index(d, ft);
 +    nrfp  = NRFP(ftype);
 +    nrfpA = interaction_function[ftype].nrfpA;
 +    strcpy(f1, formnl[nral]);
 +    strcat(f1, formlf);
 +    if ((nn = sscanf(line, f1, &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11], &c[12]))
 +        != nrfp)
 +    {
 +        if (nn == nrfpA)
 +        {
 +            /* Copy the B-state from the A-state */
 +            copy_B_from_A(ftype, c);
 +        }
 +        else
 +        {
 +            if (nn < nrfpA)
 +            {
 +                warning_error(wi, "Not enough parameters");
 +            }
 +            else if (nn > nrfpA && nn < nrfp)
 +            {
 +                warning_error(wi, "Too many parameters or not enough parameters for topology B");
 +            }
 +            else if (nn > nrfp)
 +            {
 +                warning_error(wi, "Too many parameters");
 +            }
 +            for (i = nn; (i < nrfp); i++)
 +            {
 +                c[i] = 0.0;
 +            }
 +        }
 +    }
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (at && ((p.a[i] = get_atomtype_type(alc[i], at)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown atomtype %s\n", alc[i]);
 +        }
 +        else if (bat && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown bond_atomtype %s\n", alc[i]);
 +        }
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        p.c[i] = c[i];
 +    }
 +    push_bondtype (&(bt[ftype]), &p, nral, ftype, FALSE, line, wi);
 +}
 +
 +
 +void push_dihedraltype(directive d, t_params bt[],
 +                       t_bond_atomtype bat, char *line,
 +                       warninp_t wi)
 +{
 +    const char  *formal[MAXATOMLIST+1] = {
 +        "%s",
 +        "%s%s",
 +        "%s%s%s",
 +        "%s%s%s%s",
 +        "%s%s%s%s%s",
 +        "%s%s%s%s%s%s",
 +        "%s%s%s%s%s%s%s"
 +    };
 +    const char  *formnl[MAXATOMLIST+1] = {
 +        "%*s",
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char  *formlf[MAXFORCEPARAM] = {
 +        "%lf",
 +        "%lf%lf",
 +        "%lf%lf%lf",
 +        "%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +    };
 +    int          i, ft, ftype, nn, nrfp, nrfpA, nral;
 +    char         f1[STRLEN];
 +    char         alc[MAXATOMLIST+1][20];
 +    double       c[MAXFORCEPARAM];
 +    t_param      p;
 +    gmx_bool     bAllowRepeat;
 +    char         errbuf[256];
 +
 +    /* This routine accepts dihedraltypes defined from either 2 or 4 atoms.
 +     *
 +     * We first check for 2 atoms with the 3th column being an integer
 +     * defining the type. If this isn't the case, we try it with 4 atoms
 +     * and the 5th column defining the dihedral type.
 +     */
 +    nn = sscanf(line, formal[4], alc[0], alc[1], alc[2], alc[3], alc[4]);
 +    if (nn >= 3 && strlen(alc[2]) == 1 && isdigit(alc[2][0]))
 +    {
 +        nral  = 2;
 +        ft    = strtol(alc[nral], NULL, 10);
 +        /* Move atom types around a bit and use 'X' for wildcard atoms
 +         * to create a 4-atom dihedral definition with arbitrary atoms in
 +         * position 1 and 4.
 +         */
 +        if (alc[2][0] == '2')
 +        {
 +            /* improper - the two atomtypes are 1,4. Use wildcards for 2,3 */
 +            strcpy(alc[3], alc[1]);
 +            sprintf(alc[2], "X");
 +            sprintf(alc[1], "X");
 +            /* alc[0] stays put */
 +        }
 +        else
 +        {
 +            /* proper - the two atomtypes are 2,3. Use wildcards for 1,4 */
 +            sprintf(alc[3], "X");
 +            strcpy(alc[2], alc[1]);
 +            strcpy(alc[1], alc[0]);
 +            sprintf(alc[0], "X");
 +        }
 +    }
 +    else if (nn == 5 && strlen(alc[4]) == 1 && isdigit(alc[4][0]))
 +    {
 +        nral  = 4;
 +        ft    = strtol(alc[nral], NULL, 10);
 +    }
 +    else
 +    {
 +        sprintf(errbuf, "Incorrect number of atomtypes for dihedral (%d instead of 2 or 4)", nn-1);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    if (ft == 9)
 +    {
 +        /* Previously, we have always overwritten parameters if e.g. a torsion
 +           with the same atomtypes occurs on multiple lines. However, CHARMM and
 +           some other force fields specify multiple dihedrals over some bonds,
 +           including cosines with multiplicity 6 and somethimes even higher.
 +           Thus, they cannot be represented with Ryckaert-Bellemans terms.
 +           To add support for these force fields, Dihedral type 9 is identical to
 +           normal proper dihedrals, but repeated entries are allowed.
 +         */
 +        bAllowRepeat = TRUE;
 +        ft           = 1;
 +    }
 +    else
 +    {
 +        bAllowRepeat = FALSE;
 +    }
 +
 +
 +    ftype = ifunc_index(d, ft);
 +    nrfp  = NRFP(ftype);
 +    nrfpA = interaction_function[ftype].nrfpA;
 +
 +    strcpy(f1, formnl[nral]);
 +    strcat(f1, formlf[nrfp-1]);
 +
 +    /* Check number of parameters given */
 +    if ((nn = sscanf(line, f1, &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11]))
 +        != nrfp)
 +    {
 +        if (nn == nrfpA)
 +        {
 +            /* Copy the B-state from the A-state */
 +            copy_B_from_A(ftype, c);
 +        }
 +        else
 +        {
 +            if (nn < nrfpA)
 +            {
 +                warning_error(wi, "Not enough parameters");
 +            }
 +            else if (nn > nrfpA && nn < nrfp)
 +            {
 +                warning_error(wi, "Too many parameters or not enough parameters for topology B");
 +            }
 +            else if (nn > nrfp)
 +            {
 +                warning_error(wi, "Too many parameters");
 +            }
 +            for (i = nn; (i < nrfp); i++)
 +            {
 +                c[i] = 0.0;
 +            }
 +        }
 +    }
 +
 +    for (i = 0; (i < 4); i++)
 +    {
 +        if (!strcmp(alc[i], "X"))
 +        {
 +            p.a[i] = -1;
 +        }
 +        else
 +        {
 +            if ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET)
 +            {
 +                gmx_fatal(FARGS, "Unknown bond_atomtype %s", alc[i]);
 +            }
 +        }
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        p.c[i] = c[i];
 +    }
 +    /* Always use 4 atoms here, since we created two wildcard atoms
 +     * if there wasn't of them 4 already.
 +     */
 +    push_bondtype (&(bt[ftype]), &p, 4, ftype, bAllowRepeat, line, wi);
 +}
 +
 +
 +void push_nbt(directive d, t_nbparam **nbt, gpp_atomtype_t atype,
 +              char *pline, int nb_funct,
 +              warninp_t wi)
 +{
 +    /* swap the atoms */
 +    const char *form3 = "%*s%*s%*s%lf%lf%lf";
 +    const char *form4 = "%*s%*s%*s%lf%lf%lf%lf";
 +    const char *form5 = "%*s%*s%*s%lf%lf%lf%lf%lf";
 +    char        a0[80], a1[80];
 +    int         i, f, n, ftype, nrfp;
 +    double      c[4], dum;
 +    real        cr[4];
 +    int         ai, aj;
 +    t_nbparam  *nbp;
 +    gmx_bool    bId;
 +    char        errbuf[256];
 +
 +    if (sscanf (pline, "%s%s%d", a0, a1, &f) != 3)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    ftype = ifunc_index(d, f);
 +
 +    if (ftype != nb_funct)
 +    {
 +        sprintf(errbuf, "Trying to add %s while the default nonbond type is %s",
 +                interaction_function[ftype].longname,
 +                interaction_function[nb_funct].longname);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    /* Get the force parameters */
 +    nrfp = NRFP(ftype);
 +    if (ftype == F_LJ14)
 +    {
 +        n = sscanf(pline, form4, &c[0], &c[1], &c[2], &c[3]);
 +        if (n < 2)
 +        {
 +            too_few(wi);
 +            return;
 +        }
 +        /* When the B topology parameters are not set,
 +         * copy them from topology A
 +         */
 +        GMX_ASSERT(nrfp <= 4, "LJ-14 cannot have more than 4 parameters");
 +        for (i = n; i < nrfp; i++)
 +        {
 +            c[i] = c[i-2];
 +        }
 +    }
 +    else if (ftype == F_LJC14_Q)
 +    {
 +        n = sscanf(pline, form5, &c[0], &c[1], &c[2], &c[3], &dum);
 +        if (n != 4)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else if (nrfp == 2)
 +    {
 +        if (sscanf(pline, form3, &c[0], &c[1], &dum) != 2)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else if (nrfp == 3)
 +    {
 +        if (sscanf(pline, form4, &c[0], &c[1], &c[2], &dum) != 3)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Number of force parameters for nonbonded interactions is %d"
 +                  " in file %s, line %d", nrfp, __FILE__, __LINE__);
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        cr[i] = c[i];
 +    }
 +
 +    /* Put the parameters in the matrix */
 +    if ((ai = get_atomtype_type (a0, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", a0);
 +    }
 +    if ((aj = get_atomtype_type (a1, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", a1);
 +    }
 +    nbp = &(nbt[std::max(ai, aj)][std::min(ai, aj)]);
 +
 +    if (nbp->bSet)
 +    {
 +        bId = TRUE;
 +        for (i = 0; i < nrfp; i++)
 +        {
 +            bId = bId && (nbp->c[i] == cr[i]);
 +        }
 +        if (!bId)
 +        {
 +            sprintf(errbuf, "Overriding non-bonded parameters,");
 +            warning(wi, errbuf);
 +            fprintf(stderr, "  old:");
 +            for (i = 0; i < nrfp; i++)
 +            {
 +                fprintf(stderr, " %g", nbp->c[i]);
 +            }
 +            fprintf(stderr, " new\n%s\n", pline);
 +        }
 +    }
 +    nbp->bSet = TRUE;
 +    for (i = 0; i < nrfp; i++)
 +    {
 +        nbp->c[i] = cr[i];
 +    }
 +}
 +
 +void
 +push_gb_params (gpp_atomtype_t at, char *line,
 +                warninp_t wi)
 +{
 +    int    atype;
 +    double radius, vol, surftens, gb_radius, S_hct;
 +    char   atypename[STRLEN];
 +    char   errbuf[STRLEN];
 +
 +    if ( (sscanf(line, "%s%lf%lf%lf%lf%lf", atypename, &radius, &vol, &surftens, &gb_radius, &S_hct)) != 6)
 +    {
 +        sprintf(errbuf, "Too few gb parameters for type %s\n", atypename);
 +        warning(wi, errbuf);
 +    }
 +
 +    /* Search for atomtype */
 +    atype = get_atomtype_type(atypename, at);
 +
 +    if (atype == NOTSET)
 +    {
 +        printf("Couldn't find topology match for atomtype %s\n", atypename);
 +        abort();
 +    }
 +
 +    set_atomtype_gbparam(at, atype, radius, vol, surftens, gb_radius, S_hct);
 +}
 +
 +void
 +push_cmaptype(directive d, t_params bt[], int nral, gpp_atomtype_t at,
 +              t_bond_atomtype bat, char *line,
 +              warninp_t wi)
 +{
 +    const char  *formal = "%s%s%s%s%s%s%s%s%n";
 +
 +    int          i, ft, ftype, nn, nrfp, nrfpA, nrfpB;
 +    int          start, nchar_consumed;
 +    int          nxcmap, nycmap, ncmap, read_cmap, sl, nct;
 +    char         s[20], alc[MAXATOMLIST+2][20];
 +    t_param      p;
 +    char         errbuf[256];
 +
 +    /* Keep the compiler happy */
 +    read_cmap = 0;
 +    start     = 0;
 +
 +    GMX_ASSERT(nral == 5, "CMAP requires 5 atoms per interaction");
 +
 +    /* Here we can only check for < 8 */
 +    if ((nn = sscanf(line, formal, alc[0], alc[1], alc[2], alc[3], alc[4], alc[5], alc[6], alc[7], &nchar_consumed)) < nral+3)
 +    {
 +        sprintf(errbuf, "Incorrect number of atomtypes for cmap (%d instead of 5)", nn-1);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +    start += nchar_consumed;
 +
 +    ft     = strtol(alc[nral], NULL, 10);
 +    nxcmap = strtol(alc[nral+1], NULL, 10);
 +    nycmap = strtol(alc[nral+2], NULL, 10);
 +
 +    /* Check for equal grid spacing in x and y dims */
 +    if (nxcmap != nycmap)
 +    {
 +        gmx_fatal(FARGS, "Not the same grid spacing in x and y for cmap grid: x=%d, y=%d", nxcmap, nycmap);
 +    }
 +
 +    ncmap  = nxcmap*nycmap;
 +    ftype  = ifunc_index(d, ft);
 +    nrfpA  = strtol(alc[6], NULL, 10)*strtol(alc[6], NULL, 10);
 +    nrfpB  = strtol(alc[7], NULL, 10)*strtol(alc[7], NULL, 10);
 +    nrfp   = nrfpA+nrfpB;
 +
 +    /* Allocate memory for the CMAP grid */
 +    bt[F_CMAP].ncmap += nrfp;
 +    srenew(bt[F_CMAP].cmap, bt[F_CMAP].ncmap);
 +
 +    /* Read in CMAP parameters */
 +    sl = 0;
 +    for (i = 0; i < ncmap; i++)
 +    {
 +        while (isspace(*(line+start+sl)))
 +        {
 +            sl++;
 +        }
 +        nn  = sscanf(line+start+sl, " %s ", s);
 +        sl += strlen(s);
 +        bt[F_CMAP].cmap[i+(bt[F_CMAP].ncmap)-nrfp] = strtod(s, NULL);
 +
 +        if (nn == 1)
 +        {
 +            read_cmap++;
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Error in reading cmap parameter for angle %s %s %s %s %s", alc[0], alc[1], alc[2], alc[3], alc[4]);
 +        }
 +
 +    }
 +
 +    /* Check do that we got the number of parameters we expected */
 +    if (read_cmap == nrfpA)
 +    {
 +        for (i = 0; i < ncmap; i++)
 +        {
 +            bt[F_CMAP].cmap[i+ncmap] = bt[F_CMAP].cmap[i];
 +        }
 +    }
 +    else
 +    {
 +        if (read_cmap < nrfpA)
 +        {
 +            warning_error(wi, "Not enough cmap parameters");
 +        }
 +        else if (read_cmap > nrfpA && read_cmap < nrfp)
 +        {
 +            warning_error(wi, "Too many cmap parameters or not enough parameters for topology B");
 +        }
 +        else if (read_cmap > nrfp)
 +        {
 +            warning_error(wi, "Too many cmap parameters");
 +        }
 +    }
 +
 +
 +    /* Set grid spacing and the number of grids (we assume these numbers to be the same for all grids
 +     * so we can safely assign them each time
 +     */
 +    bt[F_CMAP].grid_spacing = nxcmap;            /* Or nycmap, they need to be equal */
 +    bt[F_CMAP].nc           = bt[F_CMAP].nc + 1; /* Since we are incrementing here, we need to subtract later, see (*****) */
 +    nct                     = (nral+1) * bt[F_CMAP].nc;
 +
 +    /* Allocate memory for the cmap_types information */
 +    srenew(bt[F_CMAP].cmap_types, nct);
 +
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (at && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown atomtype %s\n", alc[i]);
 +        }
 +        else if (bat && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown bond_atomtype %s\n", alc[i]);
 +        }
 +
 +        /* Assign a grid number to each cmap_type */
 +        bt[F_CMAP].cmap_types[bt[F_CMAP].nct++] = get_bond_atomtype_type(alc[i], bat);
 +    }
 +
 +    /* Assign a type number to this cmap */
 +    bt[F_CMAP].cmap_types[bt[F_CMAP].nct++] = bt[F_CMAP].nc-1; /* Since we inremented earlier, we need to subtrac here, to get the types right (****) */
 +
 +    /* Check for the correct number of atoms (again) */
 +    if (bt[F_CMAP].nct != nct)
 +    {
 +        gmx_fatal(FARGS, "Incorrect number of atom types (%d) in cmap type %d\n", nct, bt[F_CMAP].nc);
 +    }
 +
 +    /* Is this correct?? */
 +    for (i = 0; i < MAXFORCEPARAM; i++)
 +    {
 +        p.c[i] = NOTSET;
 +    }
 +
 +    /* Push the bond to the bondlist */
 +    push_bondtype (&(bt[ftype]), &p, nral, ftype, FALSE, line, wi);
 +}
 +
 +
 +static void push_atom_now(t_symtab *symtab, t_atoms *at, int atomnr,
 +                          int atomicnumber,
 +                          int type, char *ctype, int ptype,
 +                          char *resnumberic,
 +                          char *resname, char *name, real m0, real q0,
 +                          int typeB, char *ctypeB, real mB, real qB)
 +{
 +    int           j, resind = 0, resnr;
 +    unsigned char ric;
 +    int           nr = at->nr;
 +
 +    if (((nr == 0) && (atomnr != 1)) || (nr && (atomnr != at->nr+1)))
 +    {
 +        gmx_fatal(FARGS, "Atoms in the .top are not numbered consecutively from 1 (rather, atomnr = %d, while at->nr = %d)", atomnr, at->nr);
 +    }
 +
 +    j = strlen(resnumberic) - 1;
 +    if (isdigit(resnumberic[j]))
 +    {
 +        ric = ' ';
 +    }
 +    else
 +    {
 +        ric = resnumberic[j];
 +        if (j == 0 || !isdigit(resnumberic[j-1]))
 +        {
 +            gmx_fatal(FARGS, "Invalid residue number '%s' for atom %d",
 +                      resnumberic, atomnr);
 +        }
 +    }
 +    resnr = strtol(resnumberic, NULL, 10);
 +
 +    if (nr > 0)
 +    {
 +        resind = at->atom[nr-1].resind;
 +    }
 +    if (nr == 0 || strcmp(resname, *at->resinfo[resind].name) != 0 ||
 +        resnr != at->resinfo[resind].nr ||
 +        ric   != at->resinfo[resind].ic)
 +    {
 +        if (nr == 0)
 +        {
 +            resind = 0;
 +        }
 +        else
 +        {
 +            resind++;
 +        }
 +        at->nres = resind + 1;
 +        srenew(at->resinfo, at->nres);
 +        at->resinfo[resind].name = put_symtab(symtab, resname);
 +        at->resinfo[resind].nr   = resnr;
 +        at->resinfo[resind].ic   = ric;
 +    }
 +    else
 +    {
 +        resind = at->atom[at->nr-1].resind;
 +    }
 +
 +    /* New atom instance
 +     * get new space for arrays
 +     */
 +    srenew(at->atom, nr+1);
 +    srenew(at->atomname, nr+1);
 +    srenew(at->atomtype, nr+1);
 +    srenew(at->atomtypeB, nr+1);
 +
 +    /* fill the list */
 +    at->atom[nr].type  = type;
 +    at->atom[nr].ptype = ptype;
 +    at->atom[nr].q     = q0;
 +    at->atom[nr].m     = m0;
 +    at->atom[nr].typeB = typeB;
 +    at->atom[nr].qB    = qB;
 +    at->atom[nr].mB    = mB;
 +
 +    at->atom[nr].resind     = resind;
 +    at->atom[nr].atomnumber = atomicnumber;
 +    at->atomname[nr]        = put_symtab(symtab, name);
 +    at->atomtype[nr]        = put_symtab(symtab, ctype);
 +    at->atomtypeB[nr]       = put_symtab(symtab, ctypeB);
 +    at->nr++;
 +}
 +
 +void push_cg(t_block *block, int *lastindex, int index, int a)
 +{
 +    if (debug)
 +    {
 +        fprintf (debug, "Index %d, Atom %d\n", index, a);
 +    }
 +
 +    if (((block->nr) && (*lastindex != index)) || (!block->nr))
 +    {
 +        /* add a new block */
 +        block->nr++;
 +        srenew(block->index, block->nr+1);
 +    }
 +    block->index[block->nr] = a + 1;
 +    *lastindex              = index;
 +}
 +
 +void push_atom(t_symtab *symtab, t_block *cgs,
 +               t_atoms *at, gpp_atomtype_t atype, char *line, int *lastcg,
 +               warninp_t wi)
 +{
 +    int           nr, ptype;
 +    int           cgnumber, atomnr, type, typeB, nscan;
 +    char          id[STRLEN], ctype[STRLEN], ctypeB[STRLEN],
 +                  resnumberic[STRLEN], resname[STRLEN], name[STRLEN], check[STRLEN];
 +    double        m, q, mb, qb;
 +    real          m0, q0, mB, qB;
 +
 +    /* Make a shortcut for writing in this molecule  */
 +    nr = at->nr;
 +
 +    /* Fixed parameters */
 +    if (sscanf(line, "%s%s%s%s%s%d",
 +               id, ctype, resnumberic, resname, name, &cgnumber) != 6)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    sscanf(id, "%d", &atomnr);
 +    if ((type  = get_atomtype_type(ctype, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", ctype);
 +    }
 +    ptype = get_atomtype_ptype(type, atype);
 +
 +    /* Set default from type */
 +    q0    = get_atomtype_qA(type, atype);
 +    m0    = get_atomtype_massA(type, atype);
 +    typeB = type;
 +    qB    = q0;
 +    mB    = m0;
 +
 +    /* Optional parameters */
 +    nscan = sscanf(line, "%*s%*s%*s%*s%*s%*s%lf%lf%s%lf%lf%s",
 +                   &q, &m, ctypeB, &qb, &mb, check);
 +
 +    /* Nasty switch that falls thru all the way down! */
 +    if (nscan > 0)
 +    {
 +        q0 = qB = q;
 +        if (nscan > 1)
 +        {
 +            m0 = mB = m;
 +            if (nscan > 2)
 +            {
 +                if ((typeB = get_atomtype_type(ctypeB, atype)) == NOTSET)
 +                {
 +                    gmx_fatal(FARGS, "Atomtype %s not found", ctypeB);
 +                }
 +                qB = get_atomtype_qA(typeB, atype);
 +                mB = get_atomtype_massA(typeB, atype);
 +                if (nscan > 3)
 +                {
 +                    qB = qb;
 +                    if (nscan > 4)
 +                    {
 +                        mB = mb;
 +                        if (nscan > 5)
 +                        {
 +                            warning_error(wi, "Too many parameters");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "mB=%g, qB=%g, typeB=%d\n", mB, qB, typeB);
 +    }
 +
 +    push_cg(cgs, lastcg, cgnumber, nr);
 +
 +    push_atom_now(symtab, at, atomnr, get_atomtype_atomnumber(type, atype),
 +                  type, ctype, ptype, resnumberic,
 +                  resname, name, m0, q0, typeB,
 +                  typeB == type ? ctype : ctypeB, mB, qB);
 +}
 +
 +void push_molt(t_symtab *symtab, int *nmol, t_molinfo **mol, char *line,
 +               warninp_t wi)
 +{
 +    char       type[STRLEN];
 +    int        nrexcl, i;
 +    t_molinfo *newmol;
 +
 +    if ((sscanf(line, "%s%d", type, &nrexcl)) != 2)
 +    {
 +        warning_error(wi, "Expected a molecule type name and nrexcl");
 +    }
 +
 +    /* Test if this moleculetype overwrites another */
 +    i    = 0;
 +    while (i < *nmol)
 +    {
 +        if (strcmp(*((*mol)[i].name), type) == 0)
 +        {
 +            gmx_fatal(FARGS, "moleculetype %s is redefined", type);
 +        }
 +        i++;
 +    }
 +
 +    (*nmol)++;
 +    srenew(*mol, *nmol);
 +    newmol = &((*mol)[*nmol-1]);
 +    init_molinfo(newmol);
 +
 +    /* Fill in the values */
 +    newmol->name     = put_symtab(symtab, type);
 +    newmol->nrexcl   = nrexcl;
 +    newmol->excl_set = FALSE;
 +}
 +
 +static gmx_bool default_nb_params(int ftype, t_params bt[], t_atoms *at,
 +                                  t_param *p, int c_start, gmx_bool bB, gmx_bool bGenPairs)
 +{
 +    int          i, j, ti, tj, ntype;
 +    gmx_bool     bFound;
 +    t_param     *pi    = NULL;
 +    int          nr    = bt[ftype].nr;
 +    int          nral  = NRAL(ftype);
 +    int          nrfp  = interaction_function[ftype].nrfpA;
 +    int          nrfpB = interaction_function[ftype].nrfpB;
 +
 +    if ((!bB && nrfp == 0) || (bB && nrfpB == 0))
 +    {
 +        return TRUE;
 +    }
 +
 +    bFound = FALSE;
 +    if (bGenPairs)
 +    {
 +        /* First test the generated-pair position to save
 +         * time when we have 1000*1000 entries for e.g. OPLS...
 +         */
 +        ntype = static_cast<int>(std::sqrt(static_cast<double>(nr)));
 +        GMX_ASSERT(ntype * ntype == nr, "Number of pairs of generated non-bonded parameters should be a perfect square");
 +        if (bB)
 +        {
 +            ti = at->atom[p->a[0]].typeB;
 +            tj = at->atom[p->a[1]].typeB;
 +        }
 +        else
 +        {
 +            ti = at->atom[p->a[0]].type;
 +            tj = at->atom[p->a[1]].type;
 +        }
 +        pi     = &(bt[ftype].param[ntype*ti+tj]);
 +        bFound = ((ti == pi->a[0]) && (tj == pi->a[1]));
 +    }
 +
 +    /* Search explicitly if we didnt find it */
 +    if (!bFound)
 +    {
 +        for (i = 0; ((i < nr) && !bFound); i++)
 +        {
 +            pi = &(bt[ftype].param[i]);
 +            if (bB)
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (at->atom[p->a[j]].typeB == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            else
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (at->atom[p->a[j]].type == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            bFound = (j == nral);
 +        }
 +    }
 +
 +    if (bFound)
 +    {
 +        if (bB)
 +        {
 +            if (nrfp+nrfpB > MAXFORCEPARAM)
 +            {
 +                gmx_incons("Too many force parameters");
 +            }
 +            for (j = c_start; (j < nrfpB); j++)
 +            {
 +                p->c[nrfp+j] = pi->c[j];
 +            }
 +        }
 +        else
 +        {
 +            for (j = c_start; (j < nrfp); j++)
 +            {
 +                p->c[j] = pi->c[j];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (j = c_start; (j < nrfp); j++)
 +        {
 +            p->c[j] = 0.0;
 +        }
 +    }
 +    return bFound;
 +}
 +
 +static gmx_bool default_cmap_params(t_params bondtype[],
 +                                    t_atoms *at, gpp_atomtype_t atype,
 +                                    t_param *p, gmx_bool bB,
 +                                    int *cmap_type, int *nparam_def)
 +{
 +    int      i, nparam_found;
 +    int      ct;
 +    gmx_bool bFound = FALSE;
 +
 +    nparam_found = 0;
 +    ct           = 0;
 +
 +    /* Match the current cmap angle against the list of cmap_types */
 +    for (i = 0; i < bondtype[F_CMAP].nct && !bFound; i += 6)
 +    {
 +        if (bB)
 +        {
 +
 +        }
 +        else
 +        {
 +            if (
 +                (get_atomtype_batype(at->atom[p->a[0]].type, atype) == bondtype[F_CMAP].cmap_types[i])   &&
 +                (get_atomtype_batype(at->atom[p->a[1]].type, atype) == bondtype[F_CMAP].cmap_types[i+1]) &&
 +                (get_atomtype_batype(at->atom[p->a[2]].type, atype) == bondtype[F_CMAP].cmap_types[i+2]) &&
 +                (get_atomtype_batype(at->atom[p->a[3]].type, atype) == bondtype[F_CMAP].cmap_types[i+3]) &&
 +                (get_atomtype_batype(at->atom[p->a[4]].type, atype) == bondtype[F_CMAP].cmap_types[i+4]))
 +            {
 +                /* Found cmap torsion */
 +                bFound       = TRUE;
 +                ct           = bondtype[F_CMAP].cmap_types[i+5];
 +                nparam_found = 1;
 +            }
 +        }
 +    }
 +
 +    /* If we did not find a matching type for this cmap torsion */
 +    if (!bFound)
 +    {
 +        gmx_fatal(FARGS, "Unknown cmap torsion between atoms %d %d %d %d %d\n",
 +                  p->a[0]+1, p->a[1]+1, p->a[2]+1, p->a[3]+1, p->a[4]+1);
 +    }
 +
 +    *nparam_def = nparam_found;
 +    *cmap_type  = ct;
 +
 +    return bFound;
 +}
 +
++/* Returns the number of exact atom type matches, i.e. non wild-card matches,
++ * returns -1 when there are no matches at all.
++ */
++static int natom_match(t_param *pi,
++                       int type_i, int type_j, int type_k, int type_l,
++                       const gpp_atomtype_t atype)
++{
++    if ((pi->ai() == -1 || get_atomtype_batype(type_i, atype) == pi->ai()) &&
++        (pi->aj() == -1 || get_atomtype_batype(type_j, atype) == pi->aj()) &&
++        (pi->ak() == -1 || get_atomtype_batype(type_k, atype) == pi->ak()) &&
++        (pi->al() == -1 || get_atomtype_batype(type_l, atype) == pi->al()))
++    {
++        return
++            (pi->ai() == -1 ? 0 : 1) +
++            (pi->aj() == -1 ? 0 : 1) +
++            (pi->ak() == -1 ? 0 : 1) +
++            (pi->al() == -1 ? 0 : 1);
++    }
++    else
++    {
++        return -1;
++    }
++}
++
 +static gmx_bool default_params(int ftype, t_params bt[],
 +                               t_atoms *at, gpp_atomtype_t atype,
 +                               t_param *p, gmx_bool bB,
 +                               t_param **param_def,
 +                               int *nparam_def)
 +{
-     /* We allow wildcards now. The first type (with or without wildcards) that
-      * fits is used, so you should probably put the wildcarded bondtypes
-      * at the end of each section.
-      */
++    int          nparam_found;
 +    gmx_bool     bFound, bSame;
 +    t_param     *pi    = NULL;
 +    t_param     *pj    = NULL;
 +    int          nr    = bt[ftype].nr;
 +    int          nral  = NRAL(ftype);
 +    int          nrfpA = interaction_function[ftype].nrfpA;
 +    int          nrfpB = interaction_function[ftype].nrfpB;
 +
 +    if ((!bB && nrfpA == 0) || (bB && nrfpB == 0))
 +    {
 +        return TRUE;
 +    }
 +
 +
-     /* OPLS uses 1000s of dihedraltypes, so in order to speed up the scanning we have a
-      * special case for this. Check for B state outside loop to speed it up.
-      */
 +    bFound       = FALSE;
 +    nparam_found = 0;
-         if (bB)
 +    if (ftype == F_PDIHS || ftype == F_RBDIHS || ftype == F_IDIHS || ftype == F_PIDIHS)
 +    {
-             for (i = 0; ((i < nr) && !bFound); i++)
++        int nmatch_max = -1;
++        int i          = -1;
++        int t;
++
++        /* For dihedrals we allow wildcards. We choose the first type
++         * that has the most real matches, i.e. non-wildcard matches.
++         */
++        for (t = 0; ((t < nr) && nmatch_max < 4); t++)
 +        {
-                 pi     = &(bt[ftype].param[i]);
-                 bFound =
-                     (
-                         ((pi->ai() == -1) || (get_atomtype_batype(at->atom[p->ai()].typeB, atype) == pi->ai())) &&
-                         ((pi->aj() == -1) || (get_atomtype_batype(at->atom[p->aj()].typeB, atype) == pi->aj())) &&
-                         ((pi->ak() == -1) || (get_atomtype_batype(at->atom[p->ak()].typeB, atype) == pi->ak())) &&
-                         ((pi->al() == -1) || (get_atomtype_batype(at->atom[p->al()].typeB, atype) == pi->al()))
-                     );
++            int      nmatch;
++            t_param *pt;
++
++            pt = &(bt[ftype].param[t]);
++            if (bB)
 +            {
-         }
-         else
-         {
-             /* State A */
-             for (i = 0; ((i < nr) && !bFound); i++)
++                nmatch = natom_match(pt, at->atom[p->ai()].typeB, at->atom[p->aj()].typeB, at->atom[p->ak()].typeB, at->atom[p->al()].typeB, atype);
 +            }
-                 pi     = &(bt[ftype].param[i]);
-                 bFound =
-                     (
-                         ((pi->ai() == -1) || (get_atomtype_batype(at->atom[p->ai()].type, atype) == pi->ai())) &&
-                         ((pi->aj() == -1) || (get_atomtype_batype(at->atom[p->aj()].type, atype) == pi->aj())) &&
-                         ((pi->ak() == -1) || (get_atomtype_batype(at->atom[p->ak()].type, atype) == pi->ak())) &&
-                         ((pi->al() == -1) || (get_atomtype_batype(at->atom[p->al()].type, atype) == pi->al()))
-                     );
++            else
 +            {
-         /* Find additional matches for this dihedral - necessary for ftype==9 which is used e.g. for charmm.
-          * The rules in that case is that additional matches HAVE to be on adjacent lines!
-          */
++                nmatch = natom_match(pt, at->atom[p->ai()].type, at->atom[p->aj()].type, at->atom[p->ak()].type, at->atom[p->al()].type, atype);
++            }
++            if (nmatch > nmatch_max)
++            {
++                nmatch_max = nmatch;
++                i          = t;
++                bFound     = TRUE;
 +            }
 +        }
-             for (j = i+1; j < nr && bSame; j += 2)
++
 +        if (bFound == TRUE)
 +        {
++            int j;
++
++            pi    = &(bt[ftype].param[i]);
 +            nparam_found++;
++
++            /* Find additional matches for this dihedral - necessary
++             * for ftype==9.
++             * The rule in that case is that additional matches
++             * HAVE to be on adjacent lines!
++             */
 +            bSame = TRUE;
 +            /* Continue from current i value */
++            for (j = i + 2; j < nr && bSame; j += 2)
 +            {
 +                pj    = &(bt[ftype].param[j]);
 +                bSame = (pi->ai() == pj->ai() && pi->aj() == pj->aj() && pi->ak() == pj->ak() && pi->al() == pj->al());
 +                if (bSame)
 +                {
 +                    nparam_found++;
 +                }
 +                /* nparam_found will be increased as long as the numbers match */
 +            }
 +        }
 +    }
 +    else   /* Not a dihedral */
 +    {
++        int i, j;
++
 +        for (i = 0; ((i < nr) && !bFound); i++)
 +        {
 +            pi = &(bt[ftype].param[i]);
 +            if (bB)
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (get_atomtype_batype(at->atom[p->a[j]].typeB, atype) == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            else
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (get_atomtype_batype(at->atom[p->a[j]].type, atype) == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            bFound = (j == nral);
 +        }
 +        if (bFound)
 +        {
 +            nparam_found = 1;
 +        }
 +    }
 +
 +    *param_def  = pi;
 +    *nparam_def = nparam_found;
 +
 +    return bFound;
 +}
 +
 +
 +
 +void push_bond(directive d, t_params bondtype[], t_params bond[],
 +               t_atoms *at, gpp_atomtype_t atype, char *line,
 +               gmx_bool bBonded, gmx_bool bGenPairs, real fudgeQQ,
 +               gmx_bool bZero, gmx_bool *bWarn_copy_A_B,
 +               warninp_t wi)
 +{
 +    const char  *aaformat[MAXATOMLIST] = {
 +        "%d%d",
 +        "%d%d%d",
 +        "%d%d%d%d",
 +        "%d%d%d%d%d",
 +        "%d%d%d%d%d%d",
 +        "%d%d%d%d%d%d%d"
 +    };
 +    const char  *asformat[MAXATOMLIST] = {
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char  *ccformat = "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf";
 +    int          nr, i, j, nral, nral_fmt, nread, ftype;
 +    char         format[STRLEN];
 +    /* One force parameter more, so we can check if we read too many */
 +    double       cc[MAXFORCEPARAM+1];
 +    int          aa[MAXATOMLIST+1];
 +    t_param      param, *param_defA, *param_defB;
 +    gmx_bool     bFoundA = FALSE, bFoundB = FALSE, bDef, bPert, bSwapParity = FALSE;
 +    int          nparam_defA, nparam_defB;
 +    char         errbuf[256];
 +
 +    nparam_defA = nparam_defB = 0;
 +
 +    ftype = ifunc_index(d, 1);
 +    nral  = NRAL(ftype);
 +    for (j = 0; j < MAXATOMLIST; j++)
 +    {
 +        aa[j] = NOTSET;
 +    }
 +    bDef = (NRFP(ftype) > 0);
 +
 +    if (ftype == F_SETTLE)
 +    {
 +        /* SETTLE acts on 3 atoms, but the topology format only specifies
 +         * the first atom (for historical reasons).
 +         */
 +        nral_fmt = 1;
 +    }
 +    else
 +    {
 +        nral_fmt = nral;
 +    }
 +
 +    nread = sscanf(line, aaformat[nral_fmt-1],
 +                   &aa[0], &aa[1], &aa[2], &aa[3], &aa[4], &aa[5]);
 +
 +    if (ftype == F_SETTLE)
 +    {
 +        aa[3] = aa[1];
 +        aa[1] = aa[0] + 1;
 +        aa[2] = aa[0] + 2;
 +    }
 +
 +    if (nread < nral_fmt)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    else if (nread > nral_fmt)
 +    {
 +        /* this is a hack to allow for virtual sites with swapped parity */
 +        bSwapParity = (aa[nral] < 0);
 +        if (bSwapParity)
 +        {
 +            aa[nral] = -aa[nral];
 +        }
 +        ftype = ifunc_index(d, aa[nral]);
 +        if (bSwapParity)
 +        {
 +            switch (ftype)
 +            {
 +                case F_VSITE3FAD:
 +                case F_VSITE3OUT:
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Negative function types only allowed for %s and %s",
 +                              interaction_function[F_VSITE3FAD].longname,
 +                              interaction_function[F_VSITE3OUT].longname);
 +            }
 +        }
 +    }
 +
 +
 +    /* Check for double atoms and atoms out of bounds */
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (aa[i] < 1 || aa[i] > at->nr)
 +        {
 +            gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                      "Atom index (%d) in %s out of bounds (1-%d).\n"
 +                      "This probably means that you have inserted topology section \"%s\"\n"
 +                      "in a part belonging to a different molecule than you intended to.\n"
 +                      "In that case move the \"%s\" section to the right molecule.",
 +                      get_warning_file(wi), get_warning_line(wi),
 +                      aa[i], dir2str(d), at->nr, dir2str(d), dir2str(d));
 +        }
 +        for (j = i+1; (j < nral); j++)
 +        {
 +            if (aa[i] == aa[j])
 +            {
 +                sprintf(errbuf, "Duplicate atom index (%d) in %s", aa[i], dir2str(d));
 +                warning(wi, errbuf);
 +            }
 +        }
 +    }
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = aa[j]-1;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    /* Get force params for normal and free energy perturbation
 +     * studies, as determined by types!
 +     */
 +
 +    if (bBonded)
 +    {
 +        bFoundA = default_params(ftype, bondtype, at, atype, &param, FALSE, &param_defA, &nparam_defA);
 +        if (bFoundA)
 +        {
 +            /* Copy the A-state and B-state default parameters. */
 +            GMX_ASSERT(NRFPA(ftype)+NRFPB(ftype) <= MAXFORCEPARAM, "Bonded interactions may have at most 12 parameters");
 +            for (j = 0; (j < NRFPA(ftype)+NRFPB(ftype)); j++)
 +            {
 +                param.c[j] = param_defA->c[j];
 +            }
 +        }
 +        bFoundB = default_params(ftype, bondtype, at, atype, &param, TRUE, &param_defB, &nparam_defB);
 +        if (bFoundB)
 +        {
 +            /* Copy only the B-state default parameters */
 +            for (j = NRFPA(ftype); (j < NRFP(ftype)); j++)
 +            {
 +                param.c[j] = param_defB->c[j];
 +            }
 +        }
 +    }
 +    else if (ftype == F_LJ14)
 +    {
 +        bFoundA = default_nb_params(ftype, bondtype, at, &param, 0, FALSE, bGenPairs);
 +        bFoundB = default_nb_params(ftype, bondtype, at, &param, 0, TRUE, bGenPairs);
 +    }
 +    else if (ftype == F_LJC14_Q)
 +    {
 +        param.c[0] = fudgeQQ;
 +        /* Fill in the A-state charges as default parameters */
 +        param.c[1] = at->atom[param.a[0]].q;
 +        param.c[2] = at->atom[param.a[1]].q;
 +        /* The default LJ parameters are the standard 1-4 parameters */
 +        bFoundA = default_nb_params(F_LJ14, bondtype, at, &param, 3, FALSE, bGenPairs);
 +        bFoundB = TRUE;
 +    }
 +    else if (ftype == F_LJC_PAIRS_NB)
 +    {
 +        /* Defaults are not supported here */
 +        bFoundA = FALSE;
 +        bFoundB = TRUE;
 +    }
 +    else
 +    {
 +        gmx_incons("Unknown function type in push_bond");
 +    }
 +
 +    if (nread > nral_fmt)
 +    {
 +        /* Manually specified parameters - in this case we discard multiple torsion info! */
 +
 +        strcpy(format, asformat[nral_fmt-1]);
 +        strcat(format, ccformat);
 +
 +        nread = sscanf(line, format, &cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5],
 +                       &cc[6], &cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12]);
 +
 +        if ((nread == NRFPA(ftype)) && (NRFPB(ftype) != 0))
 +        {
 +            /* We only have to issue a warning if these atoms are perturbed! */
 +            bPert = FALSE;
 +            for (j = 0; (j < nral); j++)
 +            {
 +                bPert = bPert || PERTURBED(at->atom[param.a[j]]);
 +            }
 +
 +            if (bPert && *bWarn_copy_A_B)
 +            {
 +                sprintf(errbuf,
 +                        "Some parameters for bonded interaction involving perturbed atoms are specified explicitly in state A, but not B - copying A to B");
 +                warning(wi, errbuf);
 +                *bWarn_copy_A_B = FALSE;
 +            }
 +
 +            /* If only the A parameters were specified, copy them to the B state */
 +            /* The B-state parameters correspond to the first nrfpB
 +             * A-state parameters.
 +             */
 +            for (j = 0; (j < NRFPB(ftype)); j++)
 +            {
 +                cc[nread++] = cc[j];
 +            }
 +        }
 +
 +        /* If nread was 0 or EOF, no parameters were read => use defaults.
 +         * If nread was nrfpA we copied above so nread=nrfp.
 +         * If nread was nrfp we are cool.
 +         * For F_LJC14_Q we allow supplying fudgeQQ only.
 +         * Anything else is an error!
 +         */
 +        if ((nread != 0) && (nread != EOF) && (nread != NRFP(ftype)) &&
 +            !(ftype == F_LJC14_Q && nread == 1))
 +        {
 +            gmx_fatal(FARGS, "Incorrect number of parameters - found %d, expected %d or %d for %s.",
 +                      nread, NRFPA(ftype), NRFP(ftype),
 +                      interaction_function[ftype].longname);
 +        }
 +
 +        for (j = 0; (j < nread); j++)
 +        {
 +            param.c[j] = cc[j];
 +        }
 +
 +        /* Check whether we have to use the defaults */
 +        if (nread == NRFP(ftype))
 +        {
 +            bDef = FALSE;
 +        }
 +    }
 +    else
 +    {
 +        nread = 0;
 +    }
 +    /* nread now holds the number of force parameters read! */
 +
 +    if (bDef)
 +    {
 +        /* Use defaults */
 +        /* When we have multiple terms it would be very dangerous to allow perturbations to a different atom type! */
 +        if (ftype == F_PDIHS)
 +        {
 +            if ((nparam_defA != nparam_defB) || ((nparam_defA > 1 || nparam_defB > 1) && (param_defA != param_defB)))
 +            {
 +                sprintf(errbuf,
 +                        "Cannot automatically perturb a torsion with multiple terms to different form.\n"
 +                        "Please specify perturbed parameters manually for this torsion in your topology!");
 +                warning_error(wi, errbuf);
 +            }
 +        }
 +
 +        if (nread > 0 && nread < NRFPA(ftype))
 +        {
 +            /* Issue an error, do not use defaults */
 +            sprintf(errbuf, "Not enough parameters, there should be at least %d (or 0 for defaults)", NRFPA(ftype));
 +            warning_error(wi, errbuf);
 +        }
 +
 +        if (nread == 0 || nread == EOF)
 +        {
 +            if (!bFoundA)
 +            {
 +                if (interaction_function[ftype].flags & IF_VSITE)
 +                {
 +                    /* set them to NOTSET, will be calculated later */
 +                    for (j = 0; (j < MAXFORCEPARAM); j++)
 +                    {
 +                        param.c[j] = NOTSET;
 +                    }
 +
 +                    if (bSwapParity)
 +                    {
 +                        param.c1() = -1; /* flag to swap parity of vsite construction */
 +                    }
 +                }
 +                else
 +                {
 +                    if (bZero)
 +                    {
 +                        fprintf(stderr, "NOTE: No default %s types, using zeroes\n",
 +                                interaction_function[ftype].longname);
 +                    }
 +                    else
 +                    {
 +                        sprintf(errbuf, "No default %s types", interaction_function[ftype].longname);
 +                        warning_error(wi, errbuf);
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (bSwapParity)
 +                {
 +                    switch (ftype)
 +                    {
 +                        case F_VSITE3FAD:
 +                            param.c0() = 360 - param.c0();
 +                            break;
 +                        case F_VSITE3OUT:
 +                            param.c2() = -param.c2();
 +                            break;
 +                    }
 +                }
 +            }
 +            if (!bFoundB)
 +            {
 +                /* We only have to issue a warning if these atoms are perturbed! */
 +                bPert = FALSE;
 +                for (j = 0; (j < nral); j++)
 +                {
 +                    bPert = bPert || PERTURBED(at->atom[param.a[j]]);
 +                }
 +
 +                if (bPert)
 +                {
 +                    sprintf(errbuf, "No default %s types for perturbed atoms, "
 +                            "using normal values", interaction_function[ftype].longname);
 +                    warning(wi, errbuf);
 +                }
 +            }
 +        }
 +    }
 +
 +    if ((ftype == F_PDIHS || ftype == F_ANGRES || ftype == F_ANGRESZ)
 +        && param.c[5] != param.c[2])
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             %s multiplicity can not be perturbed %f!=%f",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  interaction_function[ftype].longname,
 +                  param.c[2], param.c[5]);
 +    }
 +
 +    if (IS_TABULATED(ftype) && param.c[2] != param.c[0])
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             %s table number can not be perturbed %d!=%d",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  interaction_function[ftype].longname,
 +                  (int)(param.c[0]+0.5), (int)(param.c[2]+0.5));
 +    }
 +
 +    /* Dont add R-B dihedrals where all parameters are zero (no interaction) */
 +    if (ftype == F_RBDIHS)
 +    {
 +        nr = 0;
 +        for (i = 0; i < NRFP(ftype); i++)
 +        {
 +            if (param.c[i] != 0)
 +            {
 +                nr++;
 +            }
 +        }
 +        if (nr == 0)
 +        {
 +            return;
 +        }
 +    }
 +
 +    /* Put the values in the appropriate arrays */
 +    add_param_to_list (&bond[ftype], &param);
 +
 +    /* Push additional torsions from FF for ftype==9 if we have them.
 +     * We have already checked that the A/B states do not differ in this case,
 +     * so we do not have to double-check that again, or the vsite stuff.
 +     * In addition, those torsions cannot be automatically perturbed.
 +     */
 +    if (bDef && ftype == F_PDIHS)
 +    {
 +        for (i = 1; i < nparam_defA; i++)
 +        {
 +            /* Advance pointer! */
 +            param_defA += 2;
 +            for (j = 0; (j < NRFPA(ftype)+NRFPB(ftype)); j++)
 +            {
 +                param.c[j] = param_defA->c[j];
 +            }
 +            /* And push the next term for this torsion */
 +            add_param_to_list (&bond[ftype], &param);
 +        }
 +    }
 +}
 +
 +void push_cmap(directive d, t_params bondtype[], t_params bond[],
 +               t_atoms *at, gpp_atomtype_t atype, char *line,
 +               warninp_t wi)
 +{
 +    const char *aaformat[MAXATOMLIST+1] =
 +    {
 +        "%d",
 +        "%d%d",
 +        "%d%d%d",
 +        "%d%d%d%d",
 +        "%d%d%d%d%d",
 +        "%d%d%d%d%d%d",
 +        "%d%d%d%d%d%d%d"
 +    };
 +
 +    int      i, j, ftype, nral, nread, ncmap_params;
 +    int      cmap_type;
 +    int      aa[MAXATOMLIST+1];
 +    char     errbuf[256];
 +    gmx_bool bFound;
 +    t_param  param;
 +
 +    ftype        = ifunc_index(d, 1);
 +    nral         = NRAL(ftype);
 +    ncmap_params = 0;
 +
 +    nread = sscanf(line, aaformat[nral-1],
 +                   &aa[0], &aa[1], &aa[2], &aa[3], &aa[4], &aa[5]);
 +
 +    if (nread < nral)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    else if (nread == nral)
 +    {
 +        ftype = ifunc_index(d, 1);
 +    }
 +
 +    /* Check for double atoms and atoms out of bounds */
 +    for (i = 0; i < nral; i++)
 +    {
 +        if (aa[i] < 1 || aa[i] > at->nr)
 +        {
 +            gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                      "Atom index (%d) in %s out of bounds (1-%d).\n"
 +                      "This probably means that you have inserted topology section \"%s\"\n"
 +                      "in a part belonging to a different molecule than you intended to.\n"
 +                      "In that case move the \"%s\" section to the right molecule.",
 +                      get_warning_file(wi), get_warning_line(wi),
 +                      aa[i], dir2str(d), at->nr, dir2str(d), dir2str(d));
 +        }
 +
 +        for (j = i+1; (j < nral); j++)
 +        {
 +            if (aa[i] == aa[j])
 +            {
 +                sprintf(errbuf, "Duplicate atom index (%d) in %s", aa[i], dir2str(d));
 +                warning(wi, errbuf);
 +            }
 +        }
 +    }
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = aa[j]-1;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    /* Get the cmap type for this cmap angle */
 +    bFound = default_cmap_params(bondtype, at, atype, &param, FALSE, &cmap_type, &ncmap_params);
 +
 +    /* We want exactly one parameter (the cmap type in state A (currently no state B) back */
 +    if (bFound && ncmap_params == 1)
 +    {
 +        /* Put the values in the appropriate arrays */
 +        param.c[0] = cmap_type;
 +        add_param_to_list(&bond[ftype], &param);
 +    }
 +    else
 +    {
 +        /* This is essentially the same check as in default_cmap_params() done one more time */
 +        gmx_fatal(FARGS, "Unable to assign a cmap type to torsion %d %d %d %d and %d\n",
 +                  param.a[0]+1, param.a[1]+1, param.a[2]+1, param.a[3]+1, param.a[4]+1);
 +    }
 +}
 +
 +
 +
 +void push_vsitesn(directive d, t_params bond[],
 +                  t_atoms *at, char *line,
 +                  warninp_t wi)
 +{
 +    char   *ptr;
 +    int     type, ftype, j, n, ret, nj, a;
 +    int    *atc    = NULL;
 +    double *weight = NULL, weight_tot;
 +    t_param param;
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = NOTSET;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    ptr  = line;
 +    ret  = sscanf(ptr, "%d%n", &a, &n);
 +    ptr += n;
 +    if (ret == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             Expected an atom index in section \"%s\"",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  dir2str(d));
 +    }
 +
 +    param.a[0] = a - 1;
 +
 +    sscanf(ptr, "%d%n", &type, &n);
 +    ptr  += n;
 +    ftype = ifunc_index(d, type);
 +
 +    weight_tot = 0;
 +    nj         = 0;
 +    do
 +    {
 +        ret  = sscanf(ptr, "%d%n", &a, &n);
 +        ptr += n;
 +        if (ret > 0)
 +        {
 +            if (nj % 20 == 0)
 +            {
 +                srenew(atc, nj+20);
 +                srenew(weight, nj+20);
 +            }
 +            atc[nj] = a - 1;
 +            switch (type)
 +            {
 +                case 1:
 +                    weight[nj] = 1;
 +                    break;
 +                case 2:
 +                    /* Here we use the A-state mass as a parameter.
 +                     * Note that the B-state mass has no influence.
 +                     */
 +                    weight[nj] = at->atom[atc[nj]].m;
 +                    break;
 +                case 3:
 +                    weight[nj] = -1;
 +                    ret        = sscanf(ptr, "%lf%n", &(weight[nj]), &n);
 +                    ptr       += n;
 +                    if (weight[nj] < 0)
 +                    {
 +                        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                                  "             No weight or negative weight found for vsiten constructing atom %d (atom index %d)",
 +                                  get_warning_file(wi), get_warning_line(wi),
 +                                  nj+1, atc[nj]+1);
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Unknown vsiten type %d", type);
 +            }
 +            weight_tot += weight[nj];
 +            nj++;
 +        }
 +    }
 +    while (ret > 0);
 +
 +    if (nj == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             Expected more than one atom index in section \"%s\"",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  dir2str(d));
 +    }
 +
 +    if (weight_tot == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             The total mass of the construting atoms is zero",
 +                  get_warning_file(wi), get_warning_line(wi));
 +    }
 +
 +    for (j = 0; j < nj; j++)
 +    {
 +        param.a[1] = atc[j];
 +        param.c[0] = nj;
 +        param.c[1] = weight[j]/weight_tot;
 +        /* Put the values in the appropriate arrays */
 +        add_param_to_list (&bond[ftype], &param);
 +    }
 +
 +    sfree(atc);
 +    sfree(weight);
 +}
 +
 +void push_mol(int nrmols, t_molinfo mols[], char *pline, int *whichmol,
 +              int *nrcopies,
 +              warninp_t wi)
 +{
 +    char type[STRLEN];
 +
 +    if (sscanf(pline, "%s%d", type, nrcopies) != 2)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    /* Search moleculename.
 +     * Here we originally only did case insensitive matching. But because
 +     * some PDB files can have many chains and use case to generate more
 +     * chain-identifiers, which in turn end up in our moleculetype name,
 +     * we added support for case-sensitivity.
 +     */
 +    int nrcs    = 0;
 +    int nrci    = 0;
 +    int matchci = -1;
 +    int matchcs = -1;
 +    for (int i = 0; i < nrmols; i++)
 +    {
 +        if (strcmp(type, *(mols[i].name)) == 0)
 +        {
 +            nrcs++;
 +            matchcs = i;
 +        }
 +        if (gmx_strcasecmp(type, *(mols[i].name)) == 0)
 +        {
 +            nrci++;
 +            matchci = i;
 +        }
 +    }
 +
 +    if (nrcs == 1)
 +    {
 +        // select the case sensitive match
 +        *whichmol = matchcs;
 +    }
 +    else
 +    {
 +        // avoid matching case-insensitive when we have multiple matches
 +        if (nrci > 1)
 +        {
 +            gmx_fatal(FARGS, "For moleculetype '%s' in [ system ] %d case insensitive matches, but %d case sensitive matches were found. Check the case of the characters in the moleculetypes.", type, nrci, nrcs);
 +        }
 +        if (nrci == 1)
 +        {
 +            // select the unique case insensitive match
 +            *whichmol = matchci;
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "No such moleculetype %s", type);
 +        }
 +    }
 +}
 +
 +void init_block2(t_block2 *b2, int natom)
 +{
 +    int i;
 +
 +    b2->nr = natom;
 +    snew(b2->nra, b2->nr);
 +    snew(b2->a, b2->nr);
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        b2->a[i] = NULL;
 +    }
 +}
 +
 +void done_block2(t_block2 *b2)
 +{
 +    int i;
 +
 +    if (b2->nr)
 +    {
 +        for (i = 0; (i < b2->nr); i++)
 +        {
 +            sfree(b2->a[i]);
 +        }
 +        sfree(b2->a);
 +        sfree(b2->nra);
 +        b2->nr = 0;
 +    }
 +}
 +
 +void push_excl(char *line, t_block2 *b2)
 +{
 +    int  i, j;
 +    int  n;
 +    char base[STRLEN], format[STRLEN];
 +
 +    if (sscanf(line, "%d", &i) == 0)
 +    {
 +        return;
 +    }
 +
 +    if ((1 <= i) && (i <= b2->nr))
 +    {
 +        i--;
 +    }
 +    else
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "Unbound atom %d\n", i-1);
 +        }
 +        return;
 +    }
 +    strcpy(base, "%*d");
 +    do
 +    {
 +        strcpy(format, base);
 +        strcat(format, "%d");
 +        n = sscanf(line, format, &j);
 +        if (n == 1)
 +        {
 +            if ((1 <= j) && (j <= b2->nr))
 +            {
 +                j--;
 +                srenew(b2->a[i], ++(b2->nra[i]));
 +                b2->a[i][b2->nra[i]-1] = j;
 +                /* also add the reverse exclusion! */
 +                srenew(b2->a[j], ++(b2->nra[j]));
 +                b2->a[j][b2->nra[j]-1] = i;
 +                strcat(base, "%*d");
 +            }
 +            else
 +            {
 +                gmx_fatal(FARGS, "Invalid Atomnr j: %d, b2->nr: %d\n", j, b2->nr);
 +            }
 +        }
 +    }
 +    while (n == 1);
 +}
 +
 +void b_to_b2(t_blocka *b, t_block2 *b2)
 +{
 +    int     i;
 +    int     j, a;
 +
 +    for (i = 0; (i < b->nr); i++)
 +    {
 +        for (j = b->index[i]; (j < b->index[i+1]); j++)
 +        {
 +            a = b->a[j];
 +            srenew(b2->a[i], ++b2->nra[i]);
 +            b2->a[i][b2->nra[i]-1] = a;
 +        }
 +    }
 +}
 +
 +void b2_to_b(t_block2 *b2, t_blocka *b)
 +{
 +    int     i, nra;
 +    int     j;
 +
 +    nra = 0;
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        b->index[i] = nra;
 +        for (j = 0; (j < b2->nra[i]); j++)
 +        {
 +            b->a[nra+j] = b2->a[i][j];
 +        }
 +        nra += b2->nra[i];
 +    }
 +    /* terminate list */
 +    b->index[i] = nra;
 +}
 +
 +static int icomp(const void *v1, const void *v2)
 +{
 +    return (*((int *) v1))-(*((int *) v2));
 +}
 +
 +void merge_excl(t_blocka *excl, t_block2 *b2)
 +{
 +    int     i, k;
 +    int     j;
 +    int     nra;
 +
 +    if (!b2->nr)
 +    {
 +        return;
 +    }
 +    else if (b2->nr != excl->nr)
 +    {
 +        gmx_fatal(FARGS, "DEATH HORROR: b2->nr = %d, while excl->nr = %d",
 +                  b2->nr, excl->nr);
 +    }
 +    else if (debug)
 +    {
 +        fprintf(debug, "Entering merge_excl\n");
 +    }
 +
 +    /* First copy all entries from excl to b2 */
 +    b_to_b2(excl, b2);
 +
 +    /* Count and sort the exclusions */
 +    nra = 0;
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        if (b2->nra[i] > 0)
 +        {
 +            /* remove double entries */
 +            qsort(b2->a[i], (size_t)b2->nra[i], (size_t)sizeof(b2->a[i][0]), icomp);
 +            k = 1;
 +            for (j = 1; (j < b2->nra[i]); j++)
 +            {
 +                if (b2->a[i][j] != b2->a[i][k-1])
 +                {
 +                    b2->a[i][k] = b2->a[i][j];
 +                    k++;
 +                }
 +            }
 +            b2->nra[i] = k;
 +            nra       += b2->nra[i];
 +        }
 +    }
 +    excl->nra = nra;
 +    srenew(excl->a, excl->nra);
 +
 +    b2_to_b(b2, excl);
 +}
 +
 +int add_atomtype_decoupled(t_symtab *symtab, gpp_atomtype_t at,
 +                           t_nbparam ***nbparam, t_nbparam ***pair)
 +{
 +    t_atom  atom;
 +    t_param param;
 +    int     i, nr;
 +
 +    /* Define an atom type with all parameters set to zero (no interactions) */
 +    atom.q = 0.0;
 +    atom.m = 0.0;
 +    /* Type for decoupled atoms could be anything,
 +     * this should be changed automatically later when required.
 +     */
 +    atom.ptype = eptAtom;
 +    for (i = 0; (i < MAXFORCEPARAM); i++)
 +    {
 +        param.c[i] = 0.0;
 +    }
 +
 +    nr = add_atomtype(at, symtab, &atom, "decoupled", &param, -1, 0.0, 0.0, 0.0, 0, 0, 0);
 +
 +    /* Add space in the non-bonded parameters matrix */
 +    realloc_nb_params(at, nbparam, pair);
 +
 +    return nr;
 +}
 +
 +static void convert_pairs_to_pairsQ(t_params *plist,
 +                                    real fudgeQQ, t_atoms *atoms)
 +{
 +    t_param *paramp1, *paramp2, *paramnew;
 +    int      i, j, p1nr, p2nr, p2newnr;
 +
 +    /* Add the pair list to the pairQ list */
 +    p1nr    = plist[F_LJ14].nr;
 +    p2nr    = plist[F_LJC14_Q].nr;
 +    p2newnr = p1nr + p2nr;
 +    snew(paramnew, p2newnr);
 +
 +    paramp1             = plist[F_LJ14].param;
 +    paramp2             = plist[F_LJC14_Q].param;
 +
 +    /* Fill in the new F_LJC14_Q array with the old one. NOTE:
 +       it may be possible to just ADD the converted F_LJ14 array
 +       to the old F_LJC14_Q array, but since we have to create
 +       a new sized memory structure, better just to deep copy it all.
 +     */
 +
 +    for (i = 0; i < p2nr; i++)
 +    {
 +        /* Copy over parameters */
 +        for (j = 0; j < 5; j++) /* entries are 0-4 for F_LJC14_Q */
 +        {
 +            paramnew[i].c[j] = paramp2[i].c[j];
 +        }
 +
 +        /* copy over atoms */
 +        for (j = 0; j < 2; j++)
 +        {
 +            paramnew[i].a[j] = paramp2[i].a[j];
 +        }
 +    }
 +
 +    for (i = p2nr; i < p2newnr; i++)
 +    {
 +        j             = i-p2nr;
 +
 +        /* Copy over parameters */
 +        paramnew[i].c[0] = fudgeQQ;
 +        paramnew[i].c[1] = atoms->atom[paramp1[j].a[0]].q;
 +        paramnew[i].c[2] = atoms->atom[paramp1[j].a[1]].q;
 +        paramnew[i].c[3] = paramp1[j].c[0];
 +        paramnew[i].c[4] = paramp1[j].c[1];
 +
 +        /* copy over atoms */
 +        paramnew[i].a[0] = paramp1[j].a[0];
 +        paramnew[i].a[1] = paramp1[j].a[1];
 +    }
 +
 +    /* free the old pairlists */
 +    sfree(plist[F_LJC14_Q].param);
 +    sfree(plist[F_LJ14].param);
 +
 +    /* now assign the new data to the F_LJC14_Q structure */
 +    plist[F_LJC14_Q].param   = paramnew;
 +    plist[F_LJC14_Q].nr      = p2newnr;
 +
 +    /* Empty the LJ14 pairlist */
 +    plist[F_LJ14].nr    = 0;
 +    plist[F_LJ14].param = NULL;
 +}
 +
 +static void generate_LJCpairsNB(t_molinfo *mol, int nb_funct, t_params *nbp)
 +{
 +    int       n, ntype, i, j, k;
 +    t_atom   *atom;
 +    t_blocka *excl;
 +    gmx_bool  bExcl;
 +    t_param   param;
 +
 +    n    = mol->atoms.nr;
 +    atom = mol->atoms.atom;
 +
 +    ntype = static_cast<int>(std::sqrt(static_cast<double>(nbp->nr)));
 +    GMX_ASSERT(ntype * ntype == nbp->nr, "Number of pairs of generated non-bonded parameters should be a perfect square");
 +
 +    for (i = 0; i < MAXATOMLIST; i++)
 +    {
 +        param.a[i] = NOTSET;
 +    }
 +    for (i = 0; i < MAXFORCEPARAM; i++)
 +    {
 +        param.c[i] = NOTSET;
 +    }
 +
 +    /* Add a pair interaction for all non-excluded atom pairs */
 +    excl = &mol->excls;
 +    for (i = 0; i < n; i++)
 +    {
 +        for (j = i+1; j < n; j++)
 +        {
 +            bExcl = FALSE;
 +            for (k = excl->index[i]; k < excl->index[i+1]; k++)
 +            {
 +                if (excl->a[k] == j)
 +                {
 +                    bExcl = TRUE;
 +                }
 +            }
 +            if (!bExcl)
 +            {
 +                if (nb_funct != F_LJ)
 +                {
 +                    gmx_fatal(FARGS, "Can only generate non-bonded pair interactions for Van der Waals type Lennard-Jones");
 +                }
 +                param.a[0] = i;
 +                param.a[1] = j;
 +                param.c[0] = atom[i].q;
 +                param.c[1] = atom[j].q;
 +                param.c[2] = nbp->param[ntype*atom[i].type+atom[j].type].c[0];
 +                param.c[3] = nbp->param[ntype*atom[i].type+atom[j].type].c[1];
 +                add_param_to_list(&mol->plist[F_LJC_PAIRS_NB], &param);
 +            }
 +        }
 +    }
 +}
 +
 +static void set_excl_all(t_blocka *excl)
 +{
 +    int nat, i, j, k;
 +
 +    /* Get rid of the current exclusions and exclude all atom pairs */
 +    nat       = excl->nr;
 +    excl->nra = nat*nat;
 +    srenew(excl->a, excl->nra);
 +    k = 0;
 +    for (i = 0; i < nat; i++)
 +    {
 +        excl->index[i] = k;
 +        for (j = 0; j < nat; j++)
 +        {
 +            excl->a[k++] = j;
 +        }
 +    }
 +    excl->index[nat] = k;
 +}
 +
 +static void decouple_atoms(t_atoms *atoms, int atomtype_decouple,
 +                           int couple_lam0, int couple_lam1,
 +                           const char *mol_name)
 +{
 +    int i;
 +
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        t_atom *atom;
 +
 +        atom = &atoms->atom[i];
 +
 +        if (atom->qB != atom->q || atom->typeB != atom->type)
 +        {
 +            gmx_fatal(FARGS, "Atom %d in molecule type '%s' has different A and B state charges and/or atom types set in the topology file as well as through the mdp option '%s'. You can not use both these methods simultaneously.",
 +                      i + 1, mol_name, "couple-moltype");
 +        }
 +
 +        if (couple_lam0 == ecouplamNONE || couple_lam0 == ecouplamVDW)
 +        {
 +            atom->q     = 0.0;
 +        }
 +        if (couple_lam0 == ecouplamNONE || couple_lam0 == ecouplamQ)
 +        {
 +            atom->type  = atomtype_decouple;
 +        }
 +        if (couple_lam1 == ecouplamNONE || couple_lam1 == ecouplamVDW)
 +        {
 +            atom->qB    = 0.0;
 +        }
 +        if (couple_lam1 == ecouplamNONE || couple_lam1 == ecouplamQ)
 +        {
 +            atom->typeB = atomtype_decouple;
 +        }
 +    }
 +}
 +
 +void convert_moltype_couple(t_molinfo *mol, int atomtype_decouple, real fudgeQQ,
 +                            int couple_lam0, int couple_lam1,
 +                            gmx_bool bCoupleIntra, int nb_funct, t_params *nbp)
 +{
 +    convert_pairs_to_pairsQ(mol->plist, fudgeQQ, &mol->atoms);
 +    if (!bCoupleIntra)
 +    {
 +        generate_LJCpairsNB(mol, nb_funct, nbp);
 +        set_excl_all(&mol->excls);
 +    }
 +    decouple_atoms(&mol->atoms, atomtype_decouple, couple_lam0, couple_lam1,
 +                   *mol->name);
 +}
index 1b5c73072f7642015af465022e4cf9af0209ad19,e37b86260cf2b76f8d65a75e6973e9061e0b2635..7be65f346d891a6fa492568196dffc27b11d9a53
@@@ -36,7 -36,7 +36,7 @@@
   */
  #include "gmxpre.h"
  
 -#include "gromacs/legacyheaders/constr.h"
 +#include "constr.h"
  
  #include <assert.h>
  #include <stdlib.h>
  #include <algorithm>
  
  #include "gromacs/domdec/domdec.h"
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/essentialdynamics/edsam.h"
  #include "gromacs/fileio/confio.h"
  #include "gromacs/fileio/gmxfio.h"
  #include "gromacs/fileio/pdbio.h"
 -#include "gromacs/legacyheaders/copyrite.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/nrnb.h"
 -#include "gromacs/legacyheaders/splitter.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/gmxlib/nrnb.h"
 +#include "gromacs/math/utilities.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/mdrun.h"
 +#include "gromacs/mdlib/splitter.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/pulling/pull.h"
  #include "gromacs/topology/block.h"
  #include "gromacs/topology/invblock.h"
  #include "gromacs/topology/mtop_util.h"
 +#include "gromacs/utility/exceptions.h"
  #include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/txtdump.h"
  
  typedef struct gmx_constr {
      int                ncon_tot;       /* The total number of constraints    */
      int                maxwarn;        /* The maximum number of warnings     */
      int                warncount_lincs;
      int                warncount_settle;
 -    gmx_edsam_t        ed;            /* The essential dynamics data        */
 +    gmx_edsam_t        ed;             /* The essential dynamics data        */
  
 -    tensor            *vir_r_m_dr_th; /* Thread local working data          */
 -    int               *settle_error;  /* Thread local working data          */
 +    /* Thread local working data */
 +    tensor            *vir_r_m_dr_th;           /* Thread virial contribution */
 +    bool              *bSettleErrorHasOccurred; /* Did a settle error occur?  */
  
 -    gmx_mtop_t        *warn_mtop;     /* Only used for printing warnings    */
 +    /* Only used for printing warnings */
 +    const gmx_mtop_t  *warn_mtop;     /* Pointer to the global topology     */
  } t_gmx_constr;
  
  typedef struct {
 -    atom_id iatom[3];
 -    atom_id blocknr;
 +    int iatom[3];
 +    int blocknr;
  } t_sortblock;
  
  static int pcomp(const void *p1, const void *p2)
  {
      int          db;
 -    atom_id      min1, min2, max1, max2;
 +    int          min1, min2, max1, max2;
      t_sortblock *a1 = (t_sortblock *)p1;
      t_sortblock *a2 = (t_sortblock *)p2;
  
@@@ -175,7 -170,7 +175,7 @@@ void too_many_constraint_warnings(int e
  }
  
  static void write_constr_pdb(const char *fn, const char *title,
 -                             gmx_mtop_t *mtop,
 +                             const gmx_mtop_t *mtop,
                               int start, int homenr, t_commrec *cr,
                               rvec x[], matrix box)
  {
      gmx_fio_fclose(out);
  }
  
 -static void dump_confs(FILE *fplog, gmx_int64_t step, gmx_mtop_t *mtop,
 +static void dump_confs(FILE *fplog, gmx_int64_t step, const gmx_mtop_t *mtop,
                         int start, int homenr, t_commrec *cr,
                         rvec x[], rvec xprime[], matrix box)
  {
@@@ -283,6 -278,8 +283,6 @@@ gmx_bool constrain(FILE *fplog, gmx_boo
  {
      gmx_bool    bOK, bDump;
      int         start, homenr;
 -    int         i, j;
 -    int         settle_error;
      tensor      vir_r_m_dr;
      real        scaled_delta_t;
      real        invdt, vir_fac = 0, t;
          nth = 1;
      }
  
 -    if (nth > 1 && constr->vir_r_m_dr_th == NULL)
 -    {
 -        snew(constr->vir_r_m_dr_th, nth);
 -        snew(constr->settle_error, nth);
 -    }
 -
 -    settle_error = -1;
 -
      /* We do not need full pbc when constraints do not cross charge groups,
       * i.e. when dd->constraint_comm==NULL.
       * Note that PBC for constraints is different from PBC for bondeds.
           * by the constraint coordinate communication routine,
           * so that here we can use normal pbc.
           */
 -        pbc_null = set_pbc_dd(&pbc, ir->ePBC, cr->dd, FALSE, box);
 +        pbc_null = set_pbc_dd(&pbc, ir->ePBC,
 +                              DOMAINDECOMP(cr) ? cr->dd->nc : nullptr,
 +                              FALSE, box);
      }
      else
      {
  
      if (nsettle > 0)
      {
 -        int calcvir_atom_end;
 -
 -        if (vir == NULL)
 -        {
 -            calcvir_atom_end = 0;
 -        }
 -        else
 -        {
 -            calcvir_atom_end = md->homenr;
 -        }
 +        bool bSettleErrorHasOccurred = false;
  
          switch (econq)
          {
  #pragma omp parallel for num_threads(nth) schedule(static)
                  for (th = 0; th < nth; th++)
                  {
 -                    int start_th, end_th;
 -
 -                    if (th > 0)
 +                    try
                      {
 -                        clear_mat(constr->vir_r_m_dr_th[th]);
 +                        if (th > 0)
 +                        {
 +                            clear_mat(constr->vir_r_m_dr_th[th]);
 +                        }
  
 -                        constr->settle_error[th] = -1;
 -                    }
 -
 -                    start_th = (nsettle* th   )/nth;
 -                    end_th   = (nsettle*(th+1))/nth;
 -                    if (start_th >= 0 && end_th - start_th > 0)
 -                    {
                          csettle(constr->settled,
 -                                end_th-start_th,
 -                                settle->iatoms+start_th*(1+NRAL(F_SETTLE)),
 +                                nth, th,
                                  pbc_null,
                                  x[0], xprime[0],
 -                                invdt, v ? v[0] : NULL, calcvir_atom_end,
 +                                invdt, v ? v[0] : NULL,
 +                                vir != NULL,
                                  th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th],
 -                                th == 0 ? &settle_error : &constr->settle_error[th]);
 +                                th == 0 ? &bSettleErrorHasOccurred : &constr->bSettleErrorHasOccurred[th]);
                      }
 +                    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
                  }
                  inc_nrnb(nrnb, eNR_SETTLE, nsettle);
                  if (v != NULL)
  #pragma omp parallel for num_threads(nth) schedule(static)
                  for (th = 0; th < nth; th++)
                  {
 -                    int start_th, end_th;
 -
 -                    if (th > 0)
 -                    {
 -                        clear_mat(constr->vir_r_m_dr_th[th]);
 -                    }
 -
 -                    start_th = (nsettle* th   )/nth;
 -                    end_th   = (nsettle*(th+1))/nth;
 -
 -                    if (start_th >= 0 && end_th - start_th > 0)
 +                    try
                      {
 -                        settle_proj(constr->settled, econq,
 -                                    end_th-start_th,
 -                                    settle->iatoms+start_th*(1+NRAL(F_SETTLE)),
 -                                    pbc_null,
 -                                    x,
 -                                    xprime, min_proj, calcvir_atom_end,
 -                                    th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th]);
 +                        int calcvir_atom_end;
 +
 +                        if (vir == NULL)
 +                        {
 +                            calcvir_atom_end = 0;
 +                        }
 +                        else
 +                        {
 +                            calcvir_atom_end = md->homenr;
 +                        }
 +
 +                        if (th > 0)
 +                        {
 +                            clear_mat(constr->vir_r_m_dr_th[th]);
 +                        }
 +
 +                        int start_th = (nsettle* th   )/nth;
 +                        int end_th   = (nsettle*(th+1))/nth;
 +
 +                        if (start_th >= 0 && end_th - start_th > 0)
 +                        {
 +                            settle_proj(constr->settled, econq,
 +                                        end_th-start_th,
 +                                        settle->iatoms+start_th*(1+NRAL(F_SETTLE)),
 +                                        pbc_null,
 +                                        x,
 +                                        xprime, min_proj, calcvir_atom_end,
 +                                        th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th]);
 +                        }
                      }
 +                    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
                  }
                  /* This is an overestimate */
                  inc_nrnb(nrnb, eNR_SETTLE, nsettle);
              default:
                  gmx_incons("Unknown constraint quantity for settle");
          }
 -    }
  
 -    if (settle->nr > 0)
 -    {
          if (vir != NULL)
          {
              /* Reduce the virial contributions over the threads */
 -            for (i = 1; i < nth; i++)
 +            for (int th = 1; th < nth; th++)
              {
 -                m_add(vir_r_m_dr, constr->vir_r_m_dr_th[i], vir_r_m_dr);
 +                m_add(vir_r_m_dr, constr->vir_r_m_dr_th[th], vir_r_m_dr);
              }
          }
  
          if (econq == econqCoord)
          {
 -            for (i = 1; i < nth; i++)
 +            for (int th = 1; th < nth; th++)
              {
 -                settle_error = std::max(settle_error, constr->settle_error[i]);
 +                bSettleErrorHasOccurred = bSettleErrorHasOccurred || constr->bSettleErrorHasOccurred[th];
              }
  
 -            if (settle_error >= 0)
 +            if (bSettleErrorHasOccurred)
              {
-                 dump_confs(fplog, step, constr->warn_mtop, start, homenr, cr, x, xprime, box);
+                 char buf[256];
+                 sprintf(buf,
 -                        "\nstep " "%" GMX_PRId64 ": Water molecule starting at atom %d can not be "
 -                        "settled.\nCheck for bad contacts and/or reduce the timestep if appropriate.\n",
 -                        step, ddglatnr(cr->dd, settle->iatoms[settle_error*(1+NRAL(F_SETTLE))+1]));
++                        "\nstep " "%" GMX_PRId64 ": One or more water molecules can not be settled.\n"
++                        "Check for bad contacts and/or reduce the timestep if appropriate.\n",
++                        step);
+                 if (fplog)
+                 {
+                     fprintf(fplog, "%s", buf);
+                 }
+                 fprintf(stderr, "%s", buf);
+                 constr->warncount_settle++;
+                 if (constr->warncount_settle > constr->maxwarn)
+                 {
+                     too_many_constraint_warnings(-1, constr->warncount_settle);
+                 }
+                 bDump = TRUE;
  
-                 gmx_fatal(FARGS,
-                           "\nstep " "%" GMX_PRId64 ": One or more water molecules can not be settled.\n"
-                           "Check for bad contacts and/or reduce the timestep if appropriate.\n",
-                           step);
              }
          }
      }
          {
              vir_fac *= 2;  /* only constraining over half the distance here */
          }
 -        for (i = 0; i < DIM; i++)
 +        for (int i = 0; i < DIM; i++)
          {
 -            for (j = 0; j < DIM; j++)
 +            for (int j = 0; j < DIM; j++)
              {
                  (*vir)[i][j] = vir_fac*vir_r_m_dr[i][j];
              }
@@@ -634,11 -652,11 +645,11 @@@ real *constr_rmsd_data(struct gmx_const
      }
  }
  
 -real constr_rmsd(struct gmx_constr *constr, gmx_bool bSD2)
 +real constr_rmsd(struct gmx_constr *constr)
  {
      if (constr->lincsd)
      {
 -        return lincs_rmsd(constr->lincsd, bSD2);
 +        return lincs_rmsd(constr->lincsd);
      }
      else
      {
  }
  
  static void make_shake_sblock_serial(struct gmx_constr *constr,
 -                                     t_idef *idef, t_mdatoms *md)
 +                                     t_idef *idef, const t_mdatoms *md)
  {
      int          i, j, m, ncons;
      int          bstart, bnr;
      t_blocka     sblocks;
      t_sortblock *sb;
      t_iatom     *iatom;
 -    atom_id     *inv_sblock;
 +    int         *inv_sblock;
  
      /* Since we are processing the local topology,
       * the F_CONSTRNC ilist has been concatenated to the F_CONSTR ilist.
  }
  
  static void make_shake_sblock_dd(struct gmx_constr *constr,
 -                                 t_ilist *ilcon, t_block *cgs,
 -                                 gmx_domdec_t *dd)
 +                                 const t_ilist *ilcon, const t_block *cgs,
 +                                 const gmx_domdec_t *dd)
  {
      int      ncons, c, cg;
      t_iatom *iatom;
  }
  
  t_blocka make_at2con(int start, int natoms,
 -                     t_ilist *ilist, t_iparams *iparams,
 +                     const t_ilist *ilist, const t_iparams *iparams,
                       gmx_bool bDynamics, int *nflexiblecons)
  {
      int      *count, ncon, con, con_tot, nflexcon, ftype, i, a;
@@@ -895,17 -913,22 +906,17 @@@ static int *make_at2settle(int natoms, 
  }
  
  void set_constraints(struct gmx_constr *constr,
 -                     gmx_localtop_t *top, t_inputrec *ir,
 -                     t_mdatoms *md, t_commrec *cr)
 +                     gmx_localtop_t *top, const t_inputrec *ir,
 +                     const t_mdatoms *md, t_commrec *cr)
  {
 -    t_idef  *idef;
 -    int      ncons;
 -    t_ilist *settle;
 -    int      iO, iH;
 -
 -    idef = &top->idef;
 +    t_idef *idef = &top->idef;
  
      if (constr->ncon_tot > 0)
      {
          /* We are using the local topology,
           * so there are only F_CONSTR constraints.
           */
 -        ncons = idef->il[F_CONSTR].nr/3;
 +        int ncons = idef->il[F_CONSTR].nr/3;
  
          /* With DD we might also need to call LINCS with ncons=0 for
           * communicating coordinates to other nodes that do have constraints.
          }
      }
  
 -    if (idef->il[F_SETTLE].nr > 0 && constr->settled == NULL)
 +    if (constr->settled)
      {
 -        settle          = &idef->il[F_SETTLE];
 -        iO              = settle->iatoms[1];
 -        iH              = settle->iatoms[2];
 -        constr->settled =
 -            settle_init(md->massT[iO], md->massT[iH],
 -                        md->invmass[iO], md->invmass[iH],
 -                        idef->iparams[settle->iatoms[0]].settle.doh,
 -                        idef->iparams[settle->iatoms[0]].settle.dhh);
 +        settle_set_constraints(constr->settled,
 +                               &idef->il[F_SETTLE], md);
      }
  
      /* Make a selection of the local atoms for essential dynamics */
      }
  }
  
 -static void constr_recur(t_blocka *at2con,
 -                         t_ilist *ilist, t_iparams *iparams, gmx_bool bTopB,
 +static void constr_recur(const t_blocka *at2con,
 +                         const t_ilist *ilist, const t_iparams *iparams,
 +                         gmx_bool bTopB,
                           int at, int depth, int nc, int *path,
                           real r0, real r1, real *r2max,
                           int *count)
      }
  }
  
 -static real constr_r_max_moltype(gmx_moltype_t *molt, t_iparams *iparams,
 -                                 t_inputrec *ir)
 +static real constr_r_max_moltype(const gmx_moltype_t *molt,
 +                                 const t_iparams     *iparams,
 +                                 const t_inputrec    *ir)
  {
      int      natoms, nflexcon, *path, at, count;
  
      return rmax;
  }
  
 -real constr_r_max(FILE *fplog, gmx_mtop_t *mtop, t_inputrec *ir)
 +real constr_r_max(FILE *fplog, const gmx_mtop_t *mtop, const t_inputrec *ir)
  {
      int  mt;
      real rmax;
  }
  
  gmx_constr_t init_constraints(FILE *fplog,
 -                              gmx_mtop_t *mtop, t_inputrec *ir,
 +                              const gmx_mtop_t *mtop, const t_inputrec *ir,
                                gmx_edsam_t ed, t_state *state,
                                t_commrec *cr)
  {
 -    int                  ncon, nset, nmol, settle_type, i, mt, nflexcon;
 -    struct gmx_constr   *constr;
 -    char                *env;
 -    t_ilist             *ilist;
 -    gmx_mtop_ilistloop_t iloop;
 -
 -    ncon =
 +    int nconstraints =
          gmx_mtop_ftype_count(mtop, F_CONSTR) +
          gmx_mtop_ftype_count(mtop, F_CONSTRNC);
 -    nset = gmx_mtop_ftype_count(mtop, F_SETTLE);
 +    int nsettles =
 +        gmx_mtop_ftype_count(mtop, F_SETTLE);
  
 -    if (ncon+nset == 0 &&
 +    GMX_RELEASE_ASSERT(!ir->bPull || ir->pull_work != NULL, "init_constraints called with COM pulling before/without initializing the pull code");
 +
 +    if (nconstraints + nsettles == 0 &&
          !(ir->bPull && pull_have_constraint(ir->pull_work)) &&
          ed == NULL)
      {
          return NULL;
      }
  
 +    struct gmx_constr *constr;
 +
      snew(constr, 1);
  
 -    constr->ncon_tot = ncon;
 +    constr->ncon_tot = nconstraints;
      constr->nflexcon = 0;
 -    if (ncon > 0)
 +    if (nconstraints > 0)
      {
          constr->n_at2con_mt = mtop->nmoltype;
          snew(constr->at2con_mt, constr->n_at2con_mt);
 -        for (mt = 0; mt < mtop->nmoltype; mt++)
 +        for (int mt = 0; mt < mtop->nmoltype; mt++)
          {
 +            int nflexcon;
              constr->at2con_mt[mt] = make_at2con(0, mtop->moltype[mt].atoms.nr,
                                                  mtop->moltype[mt].ilist,
                                                  mtop->ffparams.iparams,
                                                  EI_DYNAMICS(ir->eI), &nflexcon);
 -            for (i = 0; i < mtop->nmolblock; i++)
 +            for (int i = 0; i < mtop->nmolblock; i++)
              {
                  if (mtop->molblock[i].type == mt)
                  {
          }
      }
  
 -    if (nset > 0)
 +    if (nsettles > 0)
      {
          please_cite(fplog, "Miyamoto92a");
  
          constr->bInterCGsettles = inter_charge_group_settles(mtop);
  
 -        /* Check that we have only one settle type */
 -        settle_type = -1;
 -        iloop       = gmx_mtop_ilistloop_init(mtop);
 -        while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol))
 -        {
 -            for (i = 0; i < ilist[F_SETTLE].nr; i += 4)
 -            {
 -                if (settle_type == -1)
 -                {
 -                    settle_type = ilist[F_SETTLE].iatoms[i];
 -                }
 -                else if (ilist[F_SETTLE].iatoms[i] != settle_type)
 -                {
 -                    gmx_fatal(FARGS,
 -                              "The [molecules] section of your topology specifies more than one block of\n"
 -                              "a [moleculetype] with a [settles] block. Only one such is allowed. If you\n"
 -                              "are trying to partition your solvent into different *groups* (e.g. for\n"
 -                              "freezing, T-coupling, etc.) then you are using the wrong approach. Index\n"
 -                              "files specify groups. Otherwise, you may wish to change the least-used\n"
 -                              "block of molecules with SETTLE constraints into 3 normal constraints.");
 -                }
 -            }
 -        }
 +        constr->settled         = settle_init(mtop);
  
 +        /* Make an atom to settle index for use in domain decomposition */
          constr->n_at2settle_mt = mtop->nmoltype;
          snew(constr->at2settle_mt, constr->n_at2settle_mt);
 -        for (mt = 0; mt < mtop->nmoltype; mt++)
 +        for (int mt = 0; mt < mtop->nmoltype; mt++)
          {
              constr->at2settle_mt[mt] =
                  make_at2settle(mtop->moltype[mt].atoms.nr,
                                 &mtop->moltype[mt].ilist[F_SETTLE]);
          }
 +
 +        /* Allocate thread-local work arrays */
 +        int nthreads = gmx_omp_nthreads_get(emntSETTLE);
 +        if (nthreads > 1 && constr->vir_r_m_dr_th == NULL)
 +        {
 +            snew(constr->vir_r_m_dr_th, nthreads);
 +            snew(constr->bSettleErrorHasOccurred, nthreads);
 +        }
      }
  
 -    if ((ncon + nset) > 0 && ir->epc == epcMTTK)
 +    if (nconstraints + nsettles > 0 && ir->epc == epcMTTK)
      {
          gmx_fatal(FARGS, "Constraints are not implemented with MTTK pressure control.");
      }
  
      constr->maxwarn = 999;
 -    env             = getenv("GMX_MAXCONSTRWARN");
 +    char *env       = getenv("GMX_MAXCONSTRWARN");
      if (env)
      {
          constr->maxwarn = 0;
index 2af37121559fd811d2aabb35d86dd370d6ab0584,716a29dab2571ff5ad53e46bf22a7f6f5415c700..308a8a218789652f902b34517237598943e3d7d4
  
  #include <algorithm>
  
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/nrnb.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/update.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/domdec/domdec_struct.h"
 +#include "gromacs/gmxlib/nrnb.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/invertmatrix.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/vec.h"
 -#include "gromacs/random/random.h"
 +#include "gromacs/math/vecdump.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/mdrun.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdlib/update.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/group.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/pbcutil/boxutilities.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/random/gammadistribution.h"
 +#include "gromacs/random/normaldistribution.h"
 +#include "gromacs/random/tabulatednormaldistribution.h"
 +#include "gromacs/random/threefry.h"
 +#include "gromacs/random/uniformrealdistribution.h"
 +#include "gromacs/trajectory/energy.h"
 +#include "gromacs/utility/cstringutil.h"
  #include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/smalloc.h"
  
@@@ -143,7 -131,7 +143,7 @@@ static void NHC_trotter(t_grpopts *opts
              iQinv = &(MassQ->QPinv[i*nh]);
              nd    = 1.0; /* THIS WILL CHANGE IF NOT ISOTROPIC */
              reft  = std::max<real>(0, opts->ref_t[0]);
 -            Ekin  = sqr(*veta)/MassQ->Winv;
 +            Ekin  = gmx::square(*veta)/MassQ->Winv;
          }
          else
          {
                      {
                          /* we actually don't need to update here if we save the
                             state of the GQ, but it's easier to just recompute*/
 -                        GQ[j+1] = iQinv[j+1]*((sqr(ivxi[j])/iQinv[j])-kT);
 +                        GQ[j+1] = iQinv[j+1]*((gmx::square(ivxi[j])/iQinv[j])-kT);
                      }
                      else
                      {
                      ivxi[j] = Efac*(ivxi[j]*Efac + 0.25*dt*GQ[j]);
                      if (iQinv[j+1] > 0)
                      {
 -                        GQ[j+1] = iQinv[j+1]*((sqr(ivxi[j])/iQinv[j])-kT);
 +                        GQ[j+1] = iQinv[j+1]*((gmx::square(ivxi[j])/iQinv[j])-kT);
                      }
                      else
                      {
@@@ -380,7 -368,7 +380,7 @@@ void parrinellorahman_pcoupl(FILE *fplo
  
      real   maxl;
  
 -    m_inv_ur0(box, invbox);
 +    gmx::invertBoxMatrix(box, invbox);
  
      if (!bFirstStep)
      {
@@@ -671,7 -659,6 +671,7 @@@ void berendsen_pscale(t_inputrec *ir, m
  #pragma omp parallel for num_threads(nthreads) schedule(static)
      for (n = start; n < start+nr_atoms; n++)
      {
 +        // Trivial OpenMP region that does not throw
          int g;
  
          if (cFREEZE == NULL)
@@@ -734,7 -721,7 +734,7 @@@ void berendsen_tcoupl(t_inputrec *ir, g
          if ((opts->tau_t[i] > 0) && (T > 0.0))
          {
              reft                    = std::max<real>(0, opts->ref_t[i]);
 -            lll                     = sqrt(1.0 + (dt/opts->tau_t[i])*(reft/T-1.0));
 +            lll                     = std::sqrt(1.0 + (dt/opts->tau_t[i])*(reft/T-1.0));
              ekind->tcstat[i].lambda = std::max<real>(std::min<real>(lll, 1.25), 0.8);
          }
          else
  void andersen_tcoupl(t_inputrec *ir, gmx_int64_t step,
                       const t_commrec *cr, const t_mdatoms *md, t_state *state, real rate, const gmx_bool *randomize, const real *boltzfac)
  {
 -    const int *gatindex = (DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 -    int        i;
 -    int        gc = 0;
 +    const int                                 *gatindex = (DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +    int                                        i;
 +    int                                        gc = 0;
 +    gmx::ThreeFry2x64<0>                       rng(ir->andersen_seed, gmx::RandomDomain::Thermostat);
 +    gmx::UniformRealDistribution<real>         uniformDist;
 +    gmx::TabulatedNormalDistribution<real, 14> normalDist;
  
      /* randomize the velocities of the selected particles */
  
          int      ng = gatindex ? gatindex[i] : i;
          gmx_bool bRandomize;
  
 +        rng.restart(step, ng);
 +
          if (md->cTC)
          {
              gc = md->cTC[i];  /* assign the atom to a temperature group if there are more than one */
              else
              {
                  /* Randomize particle probabilistically */
 -                double uniform[2];
 -
 -                gmx_rng_cycle_2uniform(step*2, ng, ir->andersen_seed, RND_SEED_ANDERSEN, uniform);
 -                bRandomize = (uniform[0] < rate);
 +                uniformDist.reset();
 +                bRandomize = uniformDist(rng) < rate;
              }
              if (bRandomize)
              {
 -                real scal, gauss[3];
 +                real scal;
                  int  d;
  
 -                scal = sqrt(boltzfac[gc]*md->invmass[i]);
 -                gmx_rng_cycle_3gaussian_table(step*2+1, ng, ir->andersen_seed, RND_SEED_ANDERSEN, gauss);
 +                scal = std::sqrt(boltzfac[gc]*md->invmass[i]);
 +
 +                normalDist.reset();
 +
                  for (d = 0; d < DIM; d++)
                  {
 -                    state->v[i][d] = scal*gauss[d];
 +                    state->v[i][d] = scal*normalDist(rng);
                  }
              }
          }
@@@ -1001,7 -983,7 +1001,7 @@@ extern void init_npt_masses(t_inputrec 
          {
              if ((opts->tau_t[i] > 0) && (opts->ref_t[i] > 0))
              {
 -                MassQ->Qinv[i] = 1.0/(sqr(opts->tau_t[i]/M_2PI)*opts->ref_t[i]);
 +                MassQ->Qinv[i] = 1.0/(gmx::square(opts->tau_t[i]/M_2PI)*opts->ref_t[i]);
              }
              else
              {
  
          /* units are nm^3 * ns^2 / (nm^3 * bar / kJ/mol) = kJ/mol  */
          /* Consider evaluating eventually if this the right mass to use.  All are correct, some might be more stable  */
 -        MassQ->Winv = (PRESFAC*trace(ir->compress)*BOLTZ*opts->ref_t[0])/(DIM*state->vol0*sqr(ir->tau_p/M_2PI));
 +        MassQ->Winv = (PRESFAC*trace(ir->compress)*BOLTZ*opts->ref_t[0])/(DIM*state->vol0*gmx::square(ir->tau_p/M_2PI));
          /* An alternate mass definition, from Tuckerman et al. */
 -        /* MassQ->Winv = 1.0/(sqr(ir->tau_p/M_2PI)*(opts->nrdf[0]+DIM)*BOLTZ*opts->ref_t[0]); */
 +        /* MassQ->Winv = 1.0/(gmx::square(ir->tau_p/M_2PI)*(opts->nrdf[0]+DIM)*BOLTZ*opts->ref_t[0]); */
          for (d = 0; d < DIM; d++)
          {
              for (n = 0; n < DIM; n++)
              {
 -                MassQ->Winvm[d][n] = PRESFAC*ir->compress[d][n]/(state->vol0*sqr(ir->tau_p/M_2PI));
 +                MassQ->Winvm[d][n] = PRESFAC*ir->compress[d][n]/(state->vol0*gmx::square(ir->tau_p/M_2PI));
                  /* not clear this is correct yet for the anisotropic case. Will need to reevaluate
                     before using MTTK for anisotropic states.*/
              }
          /* now, set temperature variables */
          for (i = 0; i < ngtc; i++)
          {
-             if ((opts->tau_t[i] > 0) && (opts->ref_t[i] > 0))
+             if (opts->tau_t[i] > 0 && opts->ref_t[i] > 0 && opts->nrdf[i] > 0)
              {
                  reft = std::max<real>(0, opts->ref_t[i]);
                  nd   = opts->nrdf[i];
                      {
                          ndj = 1;
                      }
 -                    MassQ->Qinv[i*nh+j]   = 1.0/(sqr(opts->tau_t[i]/M_2PI)*ndj*kT);
 +                    MassQ->Qinv[i*nh+j]   = 1.0/(gmx::square(opts->tau_t[i]/M_2PI)*ndj*kT);
                  }
              }
              else
@@@ -1120,7 -1102,7 +1120,7 @@@ int **init_npt_vars(t_inputrec *ir, t_s
  
      if (ir->eI == eiVV)
      {
 -        if (IR_NPT_TROTTER(ir))
 +        if (inputrecNptTrotter(ir))
          {
              /* This is the complicated version - there are 4 possible calls, depending on ordering.
                 We start with the initial one. */
              /* trotter_seq[4] is etrtNHC for second 1/2 step velocities - leave zero */
  
          }
 -        else if (IR_NVT_TROTTER(ir))
 +        else if (inputrecNvtTrotter(ir))
          {
              /* This is the easy version - there are only two calls, both the same.
                 Otherwise, even easier -- no calls  */
              trotter_seq[2][0] = etrtNHC;
              trotter_seq[3][0] = etrtNHC;
          }
 -        else if (IR_NPH_TROTTER(ir))
 +        else if (inputrecNphTrotter(ir))
          {
              /* This is the complicated version - there are 4 possible calls, depending on ordering.
                 We start with the initial one. */
      }
      else if (ir->eI == eiVVAK)
      {
 -        if (IR_NPT_TROTTER(ir))
 +        if (inputrecNptTrotter(ir))
          {
              /* This is the complicated version - there are 4 possible calls, depending on ordering.
                 We start with the initial one. */
              /* The second half trotter update */
              trotter_seq[4][0] = etrtNHC;
          }
 -        else if (IR_NVT_TROTTER(ir))
 +        else if (inputrecNvtTrotter(ir))
          {
              /* This is the easy version - there is only one call, both the same.
                 Otherwise, even easier -- no calls  */
              trotter_seq[1][0] = etrtNHC;
              trotter_seq[4][0] = etrtNHC;
          }
 -        else if (IR_NPH_TROTTER(ir))
 +        else if (inputrecNphTrotter(ir))
          {
              /* This is the complicated version - there are 4 possible calls, depending on ordering.
                 We start with the initial one. */
                  {
                      qmass = 1;
                  }
 -                MassQ->QPinv[i*opts->nhchainlength+j]   = 1.0/(sqr(opts->tau_t[0]/M_2PI)*qmass*kT);
 +                MassQ->QPinv[i*opts->nhchainlength+j]   = 1.0/(gmx::square(opts->tau_t[0]/M_2PI)*qmass*kT);
              }
          }
      }
@@@ -1287,7 -1269,7 +1287,7 @@@ real NPT_energy(t_inputrec *ir, t_stat
  
              case epctISOTROPIC:
                  /* contribution from the pressure momenenta */
 -                ener_npt += 0.5*sqr(state->veta)/MassQ->Winv;
 +                ener_npt += 0.5*gmx::square(state->veta)/MassQ->Winv;
  
                  /* contribution from the PV term */
                  vol       = det(state->box);
          }
      }
  
 -    if (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir))
 +    if (inputrecNptTrotter(ir) || inputrecNphTrotter(ir))
      {
          /* add the energy from the barostat thermostat chain */
          for (i = 0; i < state->nnhpres; i++)
              {
                  if (iQinv[j] > 0)
                  {
 -                    ener_npt += 0.5*sqr(ivxi[j])/iQinv[j];
 +                    ener_npt += 0.5*gmx::square(ivxi[j])/iQinv[j];
                      /* contribution from the thermal variable of the NH chain */
                      ener_npt += ixi[j]*kT;
                  }
  
              if (nd > 0.0)
              {
 -                if (IR_NVT_TROTTER(ir))
 +                if (inputrecNvtTrotter(ir))
                  {
                      /* contribution from the thermal momenta of the NH chain */
                      for (j = 0; j < nh; j++)
                      {
                          if (iQinv[j] > 0)
                          {
 -                            ener_npt += 0.5*sqr(ivxi[j])/iQinv[j];
 +                            ener_npt += 0.5*gmx::square(ivxi[j])/iQinv[j];
                              /* contribution from the thermal variable of the NH chain */
                              if (j == 0)
                              {
                  }
                  else  /* Other non Trotter temperature NH control  -- no chains yet. */
                  {
 -                    ener_npt += 0.5*BOLTZ*nd*sqr(ivxi[0])/iQinv[0];
 +                    ener_npt += 0.5*BOLTZ*nd*gmx::square(ivxi[0])/iQinv[0];
                      ener_npt += nd*ixi[0]*kT;
                  }
              }
      return ener_npt;
  }
  
 -static real vrescale_gamdev(real ia,
 -                            gmx_int64_t step, gmx_int64_t *count,
 -                            gmx_int64_t seed1, gmx_int64_t seed2)
 -/* Gamma distribution, adapted from numerical recipes */
 -{
 -    real   am, e, s, v1, v2, x, y;
 -    double rnd[2];
 -
 -    assert(ia > 1);
 -
 -    do
 -    {
 -        do
 -        {
 -            do
 -            {
 -                gmx_rng_cycle_2uniform(step, (*count)++, seed1, seed2, rnd);
 -                v1 = rnd[0];
 -                v2 = 2.0*rnd[1] - 1.0;
 -            }
 -            while (v1*v1 + v2*v2 > 1.0 ||
 -                   v1*v1*GMX_REAL_MAX < 3.0*ia);
 -            /* The last check above ensures that both x (3.0 > 2.0 in s)
 -             * and the pre-factor for e do not go out of range.
 -             */
 -            y  = v2/v1;
 -            am = ia - 1;
 -            s  = sqrt(2.0*am + 1.0);
 -            x  = s*y + am;
 -        }
 -        while (x <= 0.0);
 -
 -        e = (1.0 + y*y)*exp(am*log(x/am) - s*y);
 -
 -        gmx_rng_cycle_2uniform(step, (*count)++, seed1, seed2, rnd);
 -    }
 -    while (rnd[0] > e);
 -
 -    return x;
 -}
 -
 -static real gaussian_count(gmx_int64_t step, gmx_int64_t *count,
 -                           gmx_int64_t seed1, gmx_int64_t seed2)
 -{
 -    double rnd[2], x, y, r;
 -
 -    do
 -    {
 -        gmx_rng_cycle_2uniform(step, (*count)++, seed1, seed2, rnd);
 -        x = 2.0*rnd[0] - 1.0;
 -        y = 2.0*rnd[1] - 1.0;
 -        r = x*x + y*y;
 -    }
 -    while (r > 1.0 || r == 0.0);
 -
 -    r = sqrt(-2.0*log(r)/r);
 -
 -    return x*r;
 -}
  
 -static real vrescale_sumnoises(real nn,
 -                               gmx_int64_t step, gmx_int64_t *count,
 -                               gmx_int64_t seed1, gmx_int64_t seed2)
 +static real vrescale_sumnoises(real                            nn,
 +                               gmx_int64_t                     step,
 +                               gmx::ThreeFry2x64<>            *rng,
 +                               gmx::NormalDistribution<real>  *normalDist)
  {
  /*
   * Returns the sum of nn independent gaussian noises squared
   * (i.e. equivalent to summing the square of the return values
 - * of nn calls to gmx_rng_gaussian_real).
 + * of nn calls to a normal distribution).
   */
 -    const real ndeg_tol = 0.0001;
 -    real       r;
 +    const real                     ndeg_tol = 0.0001;
 +    real                           r;
 +    gmx::GammaDistribution<real>   gammaDist(0.5*nn, 1.0);
 +
 +    rng->restart(step, 0);
  
      if (nn < 2 + ndeg_tol)
      {
          r = 0;
          for (i = 0; i < nn_int; i++)
          {
 -            gauss = gaussian_count(step, count, seed1, seed2);
 -
 -            r += gauss*gauss;
 +            gauss = (*normalDist)(*rng);
 +            r    += gauss*gauss;
          }
      }
      else
      {
          /* Use a gamma distribution for any real nn > 2 */
 -        r = 2.0*vrescale_gamdev(0.5*nn, step, count, seed1, seed2);
 +        r = 2.0*gammaDist(*rng);
      }
  
      return r;
@@@ -1440,9 -1478,11 +1440,9 @@@ static real vrescale_resamplekin(real k
   * ndeg:  number of degrees of freedom of the atoms to be thermalized
   * taut:  relaxation time of the thermostat, in units of 'how often this routine is called'
   */
 -    /* rnd_count tracks the step-local state for the cycle random
 -     * number generator.
 -     */
 -    gmx_int64_t rnd_count = 0;
 -    real        factor, rr, ekin_new;
 +    real                           factor, rr, ekin_new;
 +    gmx::ThreeFry2x64<64>          rng(seed, gmx::RandomDomain::Thermostat);
 +    gmx::NormalDistribution<real>  normalDist;
  
      if (taut > 0.1)
      {
          factor = 0.0;
      }
  
 -    rr = gaussian_count(step, &rnd_count, seed, RND_SEED_VRESCALE);
 +    rr = normalDist(rng);
  
      ekin_new =
          kk +
 -        (1.0 - factor)*(sigma*(vrescale_sumnoises(ndeg-1, step, &rnd_count, seed, RND_SEED_VRESCALE) + rr*rr)/ndeg - kk) +
 -        2.0*rr*sqrt(kk*sigma/ndeg*(1.0 - factor)*factor);
 +        (1.0 - factor)*(sigma*(vrescale_sumnoises(ndeg-1, step, &rng, &normalDist) + rr*rr)/ndeg - kk) +
 +        2.0*rr*std::sqrt(kk*sigma/ndeg*(1.0 - factor)*factor);
  
      return ekin_new;
  }
@@@ -1500,7 -1540,7 +1500,7 @@@ void vrescale_tcoupl(t_inputrec *ir, gm
              }
              else
              {
 -                ekind->tcstat[i].lambda = sqrt(Ek_new/Ek);
 +                ekind->tcstat[i].lambda = std::sqrt(Ek_new/Ek);
              }
  
              therm_integral[i] -= Ek_new - Ek;
@@@ -1591,21 -1631,21 +1591,21 @@@ void rescale_velocities(gmx_ekindata_t 
  
  
  /* set target temperatures if we are annealing */
 -void update_annealing_target_temp(t_grpopts *opts, real t)
 +void update_annealing_target_temp(t_inputrec *ir, real t, gmx_update_t *upd)
  {
      int  i, j, n, npoints;
      real pert, thist = 0, x;
  
 -    for (i = 0; i < opts->ngtc; i++)
 +    for (i = 0; i < ir->opts.ngtc; i++)
      {
 -        npoints = opts->anneal_npoints[i];
 -        switch (opts->annealing[i])
 +        npoints = ir->opts.anneal_npoints[i];
 +        switch (ir->opts.annealing[i])
          {
              case eannNO:
                  continue;
              case  eannPERIODIC:
                  /* calculate time modulo the period */
 -                pert  = opts->anneal_time[i][npoints-1];
 +                pert  = ir->opts.anneal_time[i][npoints-1];
                  n     = static_cast<int>(t / pert);
                  thist = t - n*pert; /* modulo time */
                  /* Make sure rounding didn't get us outside the interval */
                  thist = t;
                  break;
              default:
 -                gmx_fatal(FARGS, "Death horror in update_annealing_target_temp (i=%d/%d npoints=%d)", i, opts->ngtc, npoints);
 +                gmx_fatal(FARGS, "Death horror in update_annealing_target_temp (i=%d/%d npoints=%d)", i, ir->opts.ngtc, npoints);
          }
          /* We are doing annealing for this group if we got here,
           * and we have the (relative) time as thist.
           * calculate target temp */
          j = 0;
 -        while ((j < npoints-1) && (thist > (opts->anneal_time[i][j+1])))
 +        while ((j < npoints-1) && (thist > (ir->opts.anneal_time[i][j+1])))
          {
              j++;
          }
               * Interpolate: x is the amount from j+1, (1-x) from point j
               * First treat possible jumps in temperature as a special case.
               */
 -            if ((opts->anneal_time[i][j+1]-opts->anneal_time[i][j]) < GMX_REAL_EPS*100)
 +            if ((ir->opts.anneal_time[i][j+1]-ir->opts.anneal_time[i][j]) < GMX_REAL_EPS*100)
              {
 -                opts->ref_t[i] = opts->anneal_temp[i][j+1];
 +                ir->opts.ref_t[i] = ir->opts.anneal_temp[i][j+1];
              }
              else
              {
 -                x = ((thist-opts->anneal_time[i][j])/
 -                     (opts->anneal_time[i][j+1]-opts->anneal_time[i][j]));
 -                opts->ref_t[i] = x*opts->anneal_temp[i][j+1]+(1-x)*opts->anneal_temp[i][j];
 +                x = ((thist-ir->opts.anneal_time[i][j])/
 +                     (ir->opts.anneal_time[i][j+1]-ir->opts.anneal_time[i][j]));
 +                ir->opts.ref_t[i] = x*ir->opts.anneal_temp[i][j+1]+(1-x)*ir->opts.anneal_temp[i][j];
              }
          }
          else
          {
 -            opts->ref_t[i] = opts->anneal_temp[i][npoints-1];
 +            ir->opts.ref_t[i] = ir->opts.anneal_temp[i][npoints-1];
          }
      }
 +
 +    update_temperature_constants(upd, ir);
  }
index 515af5b6cfa40ee8b9e0f41e930bd1ca7a07239c,0000000000000000000000000000000000000000..987206eec811cd15df8efd2e2e406b484504b4fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,444 -1,0 +1,444 @@@
-  * Copyright (c) 2012,2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
-             gmx_incons("The nbnxn SIMD kernels don't suport LJ-PME with LB");
++ * Copyright (c) 2012,2013,2014,2015,2016, 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.
 + */
 +/*
 + * Note: this file was generated by the Verlet kernel generator for
 + * kernel type 2xnn.
 + */
 +
 +#include "gmxpre.h"
 +
 +#include "config.h"
 +
 +#include "gromacs/math/vectypes.h"
 +#include "gromacs/mdlib/nb_verlet.h"
 +#include "gromacs/mdlib/nbnxn_simd.h"
 +#include "gromacs/mdtypes/interaction_const.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +
 +/* Include the full-width SIMD macros */
 +#include "gromacs/simd/vector_operations.h"
 +
 +#if !(GMX_SIMD_REAL_WIDTH == 8 || GMX_SIMD_REAL_WIDTH == 16)
 +#error "unsupported SIMD width"
 +#endif
 +
 +#define GMX_SIMD_J_UNROLL_SIZE 2
 +#include "nbnxn_kernel_simd_2xnn.h"
 +
 +#include "gromacs/mdlib/force_flags.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_common.h"
 +#include "gromacs/simd/simd.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/real.h"
 +
 +/*! \brief Kinds of electrostatic treatments in SIMD Verlet kernels
 + */
 +enum {
 +    coulktRF, coulktTAB, coulktTAB_TWIN, coulktEWALD, coulktEWALD_TWIN, coulktNR
 +};
 +
 +/*! \brief Kinds of Van der Waals treatments in SIMD Verlet kernels
 + */
 +enum {
 +    vdwktLJCUT_COMBGEOM, vdwktLJCUT_COMBLB, vdwktLJCUT_COMBNONE, vdwktLJFORCESWITCH, vdwktLJPOTSWITCH, vdwktLJEWALDCOMBGEOM, vdwktNR
 +};
 +
 +/* Declare and define the kernel function pointer lookup tables.
 + * The minor index of the array goes over both the LJ combination rules,
 + * which is only supported by plain cut-off, and the LJ switch/PME functions.
 + */
 +static p_nbk_func_noener p_nbk_noener[coulktNR][vdwktNR] =
 +{
 +    {
 +        nbnxn_kernel_ElecRF_VdwLJCombGeom_F_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJCombLB_F_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJ_F_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJFSw_F_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJPSw_F_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTab_VdwLJCombGeom_F_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJCombLB_F_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJ_F_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJFSw_F_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJPSw_F_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_F_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombGeom_F_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombLB_F_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJFSw_F_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJPSw_F_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEw_VdwLJCombGeom_F_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJCombLB_F_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJ_F_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJFSw_F_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJPSw_F_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJEwCombGeom_F_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_F_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_F_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJ_F_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJFSw_F_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJPSw_F_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_2xnn,
 +    },
 +};
 +
 +static p_nbk_func_ener p_nbk_ener[coulktNR][vdwktNR] =
 +{
 +    {
 +        nbnxn_kernel_ElecRF_VdwLJCombGeom_VF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJCombLB_VF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJ_VF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJFSw_VF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJPSw_VF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTab_VdwLJCombGeom_VF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJCombLB_VF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJ_VF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJFSw_VF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJPSw_VF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombLB_VF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJFSw_VF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJPSw_VF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEw_VdwLJCombGeom_VF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJCombLB_VF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJ_VF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJFSw_VF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJPSw_VF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_VF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJ_VF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJFSw_VF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJPSw_VF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_2xnn,
 +    },
 +};
 +
 +static p_nbk_func_ener p_nbk_energrp[coulktNR][vdwktNR] =
 +{
 +    {
 +        nbnxn_kernel_ElecRF_VdwLJCombGeom_VgrpF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJCombLB_VgrpF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJ_VgrpF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJFSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJPSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTab_VdwLJCombGeom_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJCombLB_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJ_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJFSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJPSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEw_VdwLJCombGeom_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJCombLB_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJ_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJFSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJPSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VgrpF_2xnn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJ_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJFSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJPSw_VgrpF_2xnn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF_2xnn,
 +    },
 +};
 +
 +
 +static void
 +reduce_group_energies(int ng, int ng_2log,
 +                      const real *VSvdw, const real *VSc,
 +                      real *Vvdw, real *Vc)
 +{
 +    const int unrollj      = GMX_SIMD_REAL_WIDTH/GMX_SIMD_J_UNROLL_SIZE;
 +    const int unrollj_half = unrollj/2;
 +    int       ng_p2, i, j, j0, j1, c, s;
 +
 +    ng_p2 = (1<<ng_2log);
 +
 +    /* The size of the x86 SIMD energy group buffer array is:
 +     * ng*ng*ng_p2*unrollj_half*simd_width
 +     */
 +    for (i = 0; i < ng; i++)
 +    {
 +        for (j = 0; j < ng; j++)
 +        {
 +            Vvdw[i*ng+j] = 0;
 +            Vc[i*ng+j]   = 0;
 +        }
 +
 +        for (j1 = 0; j1 < ng; j1++)
 +        {
 +            for (j0 = 0; j0 < ng; j0++)
 +            {
 +                c = ((i*ng + j1)*ng_p2 + j0)*unrollj_half*unrollj;
 +                for (s = 0; s < unrollj_half; s++)
 +                {
 +                    Vvdw[i*ng+j0] += VSvdw[c+0];
 +                    Vvdw[i*ng+j1] += VSvdw[c+1];
 +                    Vc  [i*ng+j0] += VSc  [c+0];
 +                    Vc  [i*ng+j1] += VSc  [c+1];
 +                    c             += unrollj + 2;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#else /* GMX_NBNXN_SIMD_2XNN */
 +
 +#include "gromacs/utility/fatalerror.h"
 +
 +#endif /* GMX_NBNXN_SIMD_2XNN */
 +
 +void
 +nbnxn_kernel_simd_2xnn(nbnxn_pairlist_set_t      gmx_unused *nbl_list,
 +                       const nbnxn_atomdata_t    gmx_unused *nbat,
 +                       const interaction_const_t gmx_unused *ic,
 +                       int                       gmx_unused  ewald_excl,
 +                       rvec                      gmx_unused *shift_vec,
 +                       int                       gmx_unused  force_flags,
 +                       int                       gmx_unused  clearF,
 +                       real                      gmx_unused *fshift,
 +                       real                      gmx_unused *Vc,
 +                       real                      gmx_unused *Vvdw)
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +{
 +    int                nnbl;
 +    nbnxn_pairlist_t **nbl;
 +    int                coulkt, vdwkt = 0;
 +    int                nb, nthreads;
 +
 +    nnbl = nbl_list->nnbl;
 +    nbl  = nbl_list->nbl;
 +
 +    if (EEL_RF(ic->eeltype) || ic->eeltype == eelCUT)
 +    {
 +        coulkt = coulktRF;
 +    }
 +    else
 +    {
 +        if (ewald_excl == ewaldexclTable)
 +        {
 +            if (ic->rcoulomb == ic->rvdw)
 +            {
 +                coulkt = coulktTAB;
 +            }
 +            else
 +            {
 +                coulkt = coulktTAB_TWIN;
 +            }
 +        }
 +        else
 +        {
 +            if (ic->rcoulomb == ic->rvdw)
 +            {
 +                coulkt = coulktEWALD;
 +            }
 +            else
 +            {
 +                coulkt = coulktEWALD_TWIN;
 +            }
 +        }
 +    }
 +
 +    if (ic->vdwtype == evdwCUT)
 +    {
 +        switch (ic->vdw_modifier)
 +        {
 +            case eintmodNONE:
 +            case eintmodPOTSHIFT:
 +                switch (nbat->comb_rule)
 +                {
 +                    case ljcrGEOM: vdwkt = vdwktLJCUT_COMBGEOM; break;
 +                    case ljcrLB:   vdwkt = vdwktLJCUT_COMBLB;   break;
 +                    case ljcrNONE: vdwkt = vdwktLJCUT_COMBNONE; break;
 +                    default:       gmx_incons("Unknown combination rule");
 +                }
 +                break;
 +            case eintmodFORCESWITCH:
 +                vdwkt = vdwktLJFORCESWITCH;
 +                break;
 +            case eintmodPOTSWITCH:
 +                vdwkt = vdwktLJPOTSWITCH;
 +                break;
 +            default:
 +                gmx_incons("Unsupported VdW interaction modifier");
 +        }
 +    }
 +    else if (ic->vdwtype == evdwPME)
 +    {
 +        if (ic->ljpme_comb_rule == eljpmeLB)
 +        {
++            gmx_incons("The nbnxn SIMD kernels don't support LJ-PME with LB");
 +        }
 +        vdwkt = vdwktLJEWALDCOMBGEOM;
 +    }
 +    else
 +    {
 +        gmx_incons("Unsupported VdW interaction type");
 +    }
 +    // cppcheck-suppress unreadVariable
 +    nthreads = gmx_omp_nthreads_get(emntNonbonded);
 +#pragma omp parallel for schedule(static) num_threads(nthreads)
 +    for (nb = 0; nb < nnbl; nb++)
 +    {
 +        // Presently, the kernels do not call C++ code that can throw, so
 +        // no need for a try/catch pair in this OpenMP region.
 +        nbnxn_atomdata_output_t *out;
 +        real                    *fshift_p;
 +
 +        out = &nbat->out[nb];
 +
 +        if (clearF == enbvClearFYes)
 +        {
 +            clear_f(nbat, nb, out->f);
 +        }
 +
 +        if ((force_flags & GMX_FORCE_VIRIAL) && nnbl == 1)
 +        {
 +            fshift_p = fshift;
 +        }
 +        else
 +        {
 +            fshift_p = out->fshift;
 +
 +            if (clearF == enbvClearFYes)
 +            {
 +                clear_fshift(fshift_p);
 +            }
 +        }
 +
 +        if (!(force_flags & GMX_FORCE_ENERGY))
 +        {
 +            /* Don't calculate energies */
 +            p_nbk_noener[coulkt][vdwkt](nbl[nb], nbat,
 +                                        ic,
 +                                        shift_vec,
 +                                        out->f,
 +                                        fshift_p);
 +        }
 +        else if (out->nV == 1)
 +        {
 +            /* No energy groups */
 +            out->Vvdw[0] = 0;
 +            out->Vc[0]   = 0;
 +
 +            p_nbk_ener[coulkt][vdwkt](nbl[nb], nbat,
 +                                      ic,
 +                                      shift_vec,
 +                                      out->f,
 +                                      fshift_p,
 +                                      out->Vvdw,
 +                                      out->Vc);
 +        }
 +        else
 +        {
 +            /* Calculate energy group contributions */
 +            int i;
 +
 +            for (i = 0; i < out->nVS; i++)
 +            {
 +                out->VSvdw[i] = 0;
 +            }
 +            for (i = 0; i < out->nVS; i++)
 +            {
 +                out->VSc[i] = 0;
 +            }
 +
 +            p_nbk_energrp[coulkt][vdwkt](nbl[nb], nbat,
 +                                         ic,
 +                                         shift_vec,
 +                                         out->f,
 +                                         fshift_p,
 +                                         out->VSvdw,
 +                                         out->VSc);
 +
 +            reduce_group_energies(nbat->nenergrp, nbat->neg_2log,
 +                                  out->VSvdw, out->VSc,
 +                                  out->Vvdw, out->Vc);
 +        }
 +    }
 +
 +    if (force_flags & GMX_FORCE_ENERGY)
 +    {
 +        reduce_energies_over_lists(nbat, nnbl, Vvdw, Vc);
 +    }
 +}
 +#else
 +{
 +    gmx_incons("nbnxn_kernel_simd_2xnn called when such kernels "
 +               " are not enabled.");
 +}
 +#endif
 +#undef GMX_SIMD_J_UNROLL_SIZE
index b57ad33376789b00fd37ea676fa1c9224a35d8ad,0000000000000000000000000000000000000000..61c00d6e46f3470225617b1471d7b3219c76ef76
mode 100644,000000..100644
--- /dev/null
@@@ -1,443 -1,0 +1,443 @@@
-  * Copyright (c) 2012,2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
-             gmx_incons("The nbnxn SIMD kernels don't suport LJ-PME with LB");
++ * Copyright (c) 2012,2013,2014,2015,2016, 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.
 + */
 +/*
 + * Note: this file was generated by the Verlet kernel generator for
 + * kernel type 4xn.
 + */
 +
 +#include "gmxpre.h"
 +
 +#include "config.h"
 +
 +#include "gromacs/math/vectypes.h"
 +#include "gromacs/mdlib/nb_verlet.h"
 +#include "gromacs/mdlib/nbnxn_simd.h"
 +#include "gromacs/mdtypes/interaction_const.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +
 +#ifdef GMX_NBNXN_SIMD_4XN
 +
 +#include "gromacs/simd/vector_operations.h"
 +
 +#if !(GMX_SIMD_REAL_WIDTH == 2 || GMX_SIMD_REAL_WIDTH == 4 || GMX_SIMD_REAL_WIDTH == 8)
 +#error "unsupported SIMD width"
 +#endif
 +
 +#define GMX_SIMD_J_UNROLL_SIZE 1
 +#include "nbnxn_kernel_simd_4xn.h"
 +
 +#include "gromacs/mdlib/force_flags.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_common.h"
 +#include "gromacs/simd/simd.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/real.h"
 +
 +/*! \brief Kinds of electrostatic treatments in SIMD Verlet kernels
 + */
 +enum {
 +    coulktRF, coulktTAB, coulktTAB_TWIN, coulktEWALD, coulktEWALD_TWIN, coulktNR
 +};
 +
 +/*! \brief Kinds of Van der Waals treatments in SIMD Verlet kernels
 + */
 +enum {
 +    vdwktLJCUT_COMBGEOM, vdwktLJCUT_COMBLB, vdwktLJCUT_COMBNONE, vdwktLJFORCESWITCH, vdwktLJPOTSWITCH, vdwktLJEWALDCOMBGEOM, vdwktNR
 +};
 +
 +/* Declare and define the kernel function pointer lookup tables.
 + * The minor index of the array goes over both the LJ combination rules,
 + * which is only supported by plain cut-off, and the LJ switch/PME functions.
 + */
 +static p_nbk_func_noener p_nbk_noener[coulktNR][vdwktNR] =
 +{
 +    {
 +        nbnxn_kernel_ElecRF_VdwLJCombGeom_F_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJCombLB_F_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJ_F_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJFSw_F_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJPSw_F_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTab_VdwLJCombGeom_F_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJCombLB_F_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJ_F_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJFSw_F_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJPSw_F_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_F_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombGeom_F_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombLB_F_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJFSw_F_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJPSw_F_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEw_VdwLJCombGeom_F_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJCombLB_F_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJ_F_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJFSw_F_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJPSw_F_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJEwCombGeom_F_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_F_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_F_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJ_F_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJFSw_F_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJPSw_F_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_4xn,
 +    },
 +};
 +
 +static p_nbk_func_ener p_nbk_ener[coulktNR][vdwktNR] =
 +{
 +    {
 +        nbnxn_kernel_ElecRF_VdwLJCombGeom_VF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJCombLB_VF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJ_VF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJFSw_VF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJPSw_VF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTab_VdwLJCombGeom_VF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJCombLB_VF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJ_VF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJFSw_VF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJPSw_VF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombLB_VF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJFSw_VF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJPSw_VF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEw_VdwLJCombGeom_VF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJCombLB_VF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJ_VF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJFSw_VF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJPSw_VF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_VF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJ_VF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJFSw_VF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJPSw_VF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_4xn,
 +    },
 +};
 +
 +static p_nbk_func_ener p_nbk_energrp[coulktNR][vdwktNR] =
 +{
 +    {
 +        nbnxn_kernel_ElecRF_VdwLJCombGeom_VgrpF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJCombLB_VgrpF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJ_VgrpF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJFSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJPSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTab_VdwLJCombGeom_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJCombLB_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJ_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJFSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJPSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEw_VdwLJCombGeom_VgrpF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJCombLB_VgrpF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJ_VgrpF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJFSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJPSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VgrpF_4xn,
 +    },
 +    {
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJ_VgrpF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJFSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJPSw_VgrpF_4xn,
 +        nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF_4xn,
 +    },
 +};
 +
 +
 +static void
 +reduce_group_energies(int ng, int ng_2log,
 +                      const real *VSvdw, const real *VSc,
 +                      real *Vvdw, real *Vc)
 +{
 +    const int unrollj      = GMX_SIMD_REAL_WIDTH/GMX_SIMD_J_UNROLL_SIZE;
 +    const int unrollj_half = unrollj/2;
 +    int       ng_p2, i, j, j0, j1, c, s;
 +
 +    ng_p2 = (1<<ng_2log);
 +
 +    /* The size of the x86 SIMD energy group buffer array is:
 +     * ng*ng*ng_p2*unrollj_half*simd_width
 +     */
 +    for (i = 0; i < ng; i++)
 +    {
 +        for (j = 0; j < ng; j++)
 +        {
 +            Vvdw[i*ng+j] = 0;
 +            Vc[i*ng+j]   = 0;
 +        }
 +
 +        for (j1 = 0; j1 < ng; j1++)
 +        {
 +            for (j0 = 0; j0 < ng; j0++)
 +            {
 +                c = ((i*ng + j1)*ng_p2 + j0)*unrollj_half*unrollj;
 +                for (s = 0; s < unrollj_half; s++)
 +                {
 +                    Vvdw[i*ng+j0] += VSvdw[c+0];
 +                    Vvdw[i*ng+j1] += VSvdw[c+1];
 +                    Vc  [i*ng+j0] += VSc  [c+0];
 +                    Vc  [i*ng+j1] += VSc  [c+1];
 +                    c             += unrollj + 2;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#else /* GMX_NBNXN_SIMD_4XN */
 +
 +#include "gromacs/utility/fatalerror.h"
 +
 +#endif /* GMX_NBNXN_SIMD_4XN */
 +
 +void
 +nbnxn_kernel_simd_4xn(nbnxn_pairlist_set_t      gmx_unused *nbl_list,
 +                      const nbnxn_atomdata_t    gmx_unused *nbat,
 +                      const interaction_const_t gmx_unused *ic,
 +                      int                       gmx_unused  ewald_excl,
 +                      rvec                      gmx_unused *shift_vec,
 +                      int                       gmx_unused  force_flags,
 +                      int                       gmx_unused  clearF,
 +                      real                      gmx_unused *fshift,
 +                      real                      gmx_unused *Vc,
 +                      real                      gmx_unused *Vvdw)
 +#ifdef GMX_NBNXN_SIMD_4XN
 +{
 +    int                nnbl;
 +    nbnxn_pairlist_t **nbl;
 +    int                coulkt, vdwkt = 0;
 +    int                nb, nthreads;
 +
 +    nnbl = nbl_list->nnbl;
 +    nbl  = nbl_list->nbl;
 +
 +    if (EEL_RF(ic->eeltype) || ic->eeltype == eelCUT)
 +    {
 +        coulkt = coulktRF;
 +    }
 +    else
 +    {
 +        if (ewald_excl == ewaldexclTable)
 +        {
 +            if (ic->rcoulomb == ic->rvdw)
 +            {
 +                coulkt = coulktTAB;
 +            }
 +            else
 +            {
 +                coulkt = coulktTAB_TWIN;
 +            }
 +        }
 +        else
 +        {
 +            if (ic->rcoulomb == ic->rvdw)
 +            {
 +                coulkt = coulktEWALD;
 +            }
 +            else
 +            {
 +                coulkt = coulktEWALD_TWIN;
 +            }
 +        }
 +    }
 +
 +    if (ic->vdwtype == evdwCUT)
 +    {
 +        switch (ic->vdw_modifier)
 +        {
 +            case eintmodNONE:
 +            case eintmodPOTSHIFT:
 +                switch (nbat->comb_rule)
 +                {
 +                    case ljcrGEOM: vdwkt = vdwktLJCUT_COMBGEOM; break;
 +                    case ljcrLB:   vdwkt = vdwktLJCUT_COMBLB;   break;
 +                    case ljcrNONE: vdwkt = vdwktLJCUT_COMBNONE; break;
 +                    default:       gmx_incons("Unknown combination rule");
 +                }
 +                break;
 +            case eintmodFORCESWITCH:
 +                vdwkt = vdwktLJFORCESWITCH;
 +                break;
 +            case eintmodPOTSWITCH:
 +                vdwkt = vdwktLJPOTSWITCH;
 +                break;
 +            default:
 +                gmx_incons("Unsupported VdW interaction modifier");
 +        }
 +    }
 +    else if (ic->vdwtype == evdwPME)
 +    {
 +        if (ic->ljpme_comb_rule == eljpmeLB)
 +        {
++            gmx_incons("The nbnxn SIMD kernels don't support LJ-PME with LB");
 +        }
 +        vdwkt = vdwktLJEWALDCOMBGEOM;
 +    }
 +    else
 +    {
 +        gmx_incons("Unsupported VdW interaction type");
 +    }
 +    // cppcheck-suppress unreadVariable
 +    nthreads = gmx_omp_nthreads_get(emntNonbonded);
 +#pragma omp parallel for schedule(static) num_threads(nthreads)
 +    for (nb = 0; nb < nnbl; nb++)
 +    {
 +        // Presently, the kernels do not call C++ code that can throw, so
 +        // no need for a try/catch pair in this OpenMP region.
 +        nbnxn_atomdata_output_t *out;
 +        real                    *fshift_p;
 +
 +        out = &nbat->out[nb];
 +
 +        if (clearF == enbvClearFYes)
 +        {
 +            clear_f(nbat, nb, out->f);
 +        }
 +
 +        if ((force_flags & GMX_FORCE_VIRIAL) && nnbl == 1)
 +        {
 +            fshift_p = fshift;
 +        }
 +        else
 +        {
 +            fshift_p = out->fshift;
 +
 +            if (clearF == enbvClearFYes)
 +            {
 +                clear_fshift(fshift_p);
 +            }
 +        }
 +
 +        if (!(force_flags & GMX_FORCE_ENERGY))
 +        {
 +            /* Don't calculate energies */
 +            p_nbk_noener[coulkt][vdwkt](nbl[nb], nbat,
 +                                        ic,
 +                                        shift_vec,
 +                                        out->f,
 +                                        fshift_p);
 +        }
 +        else if (out->nV == 1)
 +        {
 +            /* No energy groups */
 +            out->Vvdw[0] = 0;
 +            out->Vc[0]   = 0;
 +
 +            p_nbk_ener[coulkt][vdwkt](nbl[nb], nbat,
 +                                      ic,
 +                                      shift_vec,
 +                                      out->f,
 +                                      fshift_p,
 +                                      out->Vvdw,
 +                                      out->Vc);
 +        }
 +        else
 +        {
 +            /* Calculate energy group contributions */
 +            int i;
 +
 +            for (i = 0; i < out->nVS; i++)
 +            {
 +                out->VSvdw[i] = 0;
 +            }
 +            for (i = 0; i < out->nVS; i++)
 +            {
 +                out->VSc[i] = 0;
 +            }
 +
 +            p_nbk_energrp[coulkt][vdwkt](nbl[nb], nbat,
 +                                         ic,
 +                                         shift_vec,
 +                                         out->f,
 +                                         fshift_p,
 +                                         out->VSvdw,
 +                                         out->VSc);
 +
 +            reduce_group_energies(nbat->nenergrp, nbat->neg_2log,
 +                                  out->VSvdw, out->VSc,
 +                                  out->Vvdw, out->Vc);
 +        }
 +    }
 +
 +    if (force_flags & GMX_FORCE_ENERGY)
 +    {
 +        reduce_energies_over_lists(nbat, nnbl, Vvdw, Vc);
 +    }
 +}
 +#else
 +{
 +    gmx_incons("nbnxn_kernel_simd_4xn called when such kernels "
 +               " are not enabled.");
 +}
 +#endif
 +#undef GMX_SIMD_J_UNROLL_SIZE
index 10034f3e93a31667cd6bf885b49d24689b60aece,ac13d64b9392aef451aa29cd444b2331a266b1af..45dfd81fddd48d3b5a5b3461f77626e64984026e
@@@ -36,7 -36,7 +36,7 @@@
   */
  #include "gmxpre.h"
  
 -#include "gromacs/legacyheaders/sim_util.h"
 +#include "sim_util.h"
  
  #include "config.h"
  
  #include <stdio.h>
  #include <string.h>
  
 +#include <array>
 +
  #include "gromacs/domdec/domdec.h"
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/essentialdynamics/edsam.h"
  #include "gromacs/ewald/pme.h"
 +#include "gromacs/gmxlib/chargegroup.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gmxlib/nrnb.h"
  #include "gromacs/gmxlib/nonbonded/nb_free_energy.h"
  #include "gromacs/gmxlib/nonbonded/nb_kernel.h"
 +#include "gromacs/gmxlib/nonbonded/nonbonded.h"
  #include "gromacs/imd/imd.h"
 -#include "gromacs/legacyheaders/calcmu.h"
 -#include "gromacs/legacyheaders/chargegroup.h"
 -#include "gromacs/legacyheaders/constr.h"
 -#include "gromacs/legacyheaders/copyrite.h"
 -#include "gromacs/legacyheaders/disre.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/genborn.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/mdatoms.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/nonbonded.h"
 -#include "gromacs/legacyheaders/nrnb.h"
 -#include "gromacs/legacyheaders/orires.h"
 -#include "gromacs/legacyheaders/qmmm.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/update.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
  #include "gromacs/listed-forces/bonded.h"
 +#include "gromacs/listed-forces/disre.h"
 +#include "gromacs/listed-forces/orires.h"
 +#include "gromacs/math/functions.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/math/vecdump.h"
 +#include "gromacs/mdlib/calcmu.h"
 +#include "gromacs/mdlib/constr.h"
 +#include "gromacs/mdlib/force.h"
 +#include "gromacs/mdlib/forcerec.h"
 +#include "gromacs/mdlib/genborn.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/mdrun.h"
  #include "gromacs/mdlib/nb_verlet.h"
  #include "gromacs/mdlib/nbnxn_atomdata.h"
  #include "gromacs/mdlib/nbnxn_gpu_data_mgmt.h"
 +#include "gromacs/mdlib/nbnxn_grid.h"
  #include "gromacs/mdlib/nbnxn_search.h"
 +#include "gromacs/mdlib/qmmm.h"
 +#include "gromacs/mdlib/update.h"
  #include "gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_gpu_ref.h"
  #include "gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_ref.h"
  #include "gromacs/mdlib/nbnxn_kernels/simd_2xnn/nbnxn_kernel_simd_2xnn.h"
  #include "gromacs/mdlib/nbnxn_kernels/simd_4xn/nbnxn_kernel_simd_4xn.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/pbcutil/ishift.h"
  #include "gromacs/pbcutil/mshift.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/pulling/pull.h"
  #include "gromacs/pulling/pull_rotation.h"
 +#include "gromacs/timing/cyclecounter.h"
  #include "gromacs/timing/gpu_timing.h"
  #include "gromacs/timing/wallcycle.h"
 +#include "gromacs/timing/wallcyclereporting.h"
  #include "gromacs/timing/walltime_accounting.h"
 +#include "gromacs/utility/basedefinitions.h"
  #include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
  #include "gromacs/utility/sysinfo.h"
  
 -#include "adress.h"
  #include "nbnxn_gpu.h"
  
 +static const bool useCuda   = GMX_GPU == GMX_GPU_CUDA;
 +static const bool useOpenCL = GMX_GPU == GMX_GPU_OPENCL;
 +
  void print_time(FILE                     *out,
                  gmx_walltime_accounting_t walltime_accounting,
                  gmx_int64_t               step,
      double dt, elapsed_seconds, time_per_step;
      char   buf[48];
  
 -#ifndef GMX_THREAD_MPI
 +#if !GMX_THREAD_MPI
      if (!PAR(cr))
  #endif
      {
                      ir->delta_t/1000*24*60*60/time_per_step);
          }
      }
 -#ifndef GMX_THREAD_MPI
 +#if !GMX_THREAD_MPI
      if (PAR(cr))
      {
          fprintf(out, "\n");
@@@ -255,11 -242,11 +255,11 @@@ static void calc_f_el(FILE *fp, int  st
              if (Et[m].n == 3)
              {
                  t0     = Et[m].a[1];
 -                Ext[m] = cos(Et[m].a[0]*(t-t0))*exp(-sqr(t-t0)/(2.0*sqr(Et[m].a[2])));
 +                Ext[m] = std::cos(Et[m].a[0]*(t-t0))*std::exp(-gmx::square(t-t0)/(2.0*gmx::square(Et[m].a[2])));
              }
              else
              {
 -                Ext[m] = cos(Et[m].a[0]*t);
 +                Ext[m] = std::cos(Et[m].a[0]*t);
              }
          }
          else
@@@ -390,7 -377,7 +390,7 @@@ static void print_large_forces(FILE *fp
      real pf2, fn2;
      char buf[STEPSTRSIZE];
  
 -    pf2 = sqr(pforce);
 +    pf2 = gmx::square(pforce);
      for (i = 0; i < md->homenr; i++)
      {
          fn2 = norm2(f[i]);
          {
              fprintf(fp, "step %s  atom %6d  x %8.3f %8.3f %8.3f  force %12.5e\n",
                      gmx_step_str(step, buf),
 -                    ddglatnr(cr->dd, i), x[i][XX], x[i][YY], x[i][ZZ], sqrt(fn2));
 +                    ddglatnr(cr->dd, i), x[i][XX], x[i][YY], x[i][ZZ], std::sqrt(fn2));
          }
      }
  }
@@@ -652,6 -639,10 +652,6 @@@ static void do_nb_verlet_fep(nbnxn_pair
      {
          donb_flags |= GMX_NONBONDED_DO_POTENTIAL;
      }
 -    if (flags & GMX_FORCE_DO_LR)
 -    {
 -        donb_flags |= GMX_NONBONDED_DO_LR;
 -    }
  
      kernel_data.flags  = donb_flags;
      kernel_data.lambda = lambda;
  #pragma omp parallel for schedule(static) num_threads(nbl_lists->nnbl)
      for (th = 0; th < nbl_lists->nnbl; th++)
      {
 -        gmx_nb_free_energy_kernel(nbl_lists->nbl_fep[th],
 -                                  x, f, fr, mdatoms, &kernel_data, nrnb);
 +        try
 +        {
 +            gmx_nb_free_energy_kernel(nbl_lists->nbl_fep[th],
 +                                      x, f, fr, mdatoms, &kernel_data, nrnb);
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
      }
  
      if (fepvals->sc_alpha != 0)
  #pragma omp parallel for schedule(static) num_threads(nbl_lists->nnbl)
              for (th = 0; th < nbl_lists->nnbl; th++)
              {
 -                gmx_nb_free_energy_kernel(nbl_lists->nbl_fep[th],
 -                                          x, f, fr, mdatoms, &kernel_data, nrnb);
 +                try
 +                {
 +                    gmx_nb_free_energy_kernel(nbl_lists->nbl_fep[th],
 +                                              x, f, fr, mdatoms, &kernel_data, nrnb);
 +                }
 +                GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
              }
  
              sum_epot(&(enerd->foreign_grpp), enerd->foreign_term);
@@@ -754,14 -737,10 +754,14 @@@ void do_force_cutsVERLET(FILE *fplog, t
      int                 start, homenr;
      double              mu[2*DIM];
      gmx_bool            bStateChanged, bNS, bFillGrid, bCalcCGCM;
 -    gmx_bool            bDoLongRange, bDoForces, bSepLRF, bUseGPU, bUseOrEmulGPU;
 +    gmx_bool            bDoForces, bUseGPU, bUseOrEmulGPU;
      gmx_bool            bDiffKernels = FALSE;
      rvec                vzero, box_diag;
      float               cycles_pme, cycles_force, cycles_wait_gpu;
 +    /* TODO To avoid loss of precision, float can't be used for a
 +     * cycle count. Build an object that can do this right and perhaps
 +     * also be used by gmx_wallcycle_t */
 +    gmx_cycles_t        cycleCountBeforeLocalWorkCompletes = 0;
      nonbonded_verlet_t *nbv;
  
      cycles_force    = 0;
      bNS           = (flags & GMX_FORCE_NS) && (fr->bAllvsAll == FALSE);
      bFillGrid     = (bNS && bStateChanged);
      bCalcCGCM     = (bFillGrid && !DOMAINDECOMP(cr));
 -    bDoLongRange  = (fr->bTwinRange && bNS && (flags & GMX_FORCE_DO_LR));
      bDoForces     = (flags & GMX_FORCE_FORCES);
 -    bSepLRF       = (bDoLongRange && bDoForces && (flags & GMX_FORCE_SEPLRF));
      bUseGPU       = fr->nbv->bUseGPU;
      bUseOrEmulGPU = bUseGPU || (nbv->grp[0].kernel_type == nbnxnk8x8x8_PlainC);
  
      {
          update_forcerec(fr, box);
  
 -        if (NEED_MUTOT(*inputrec))
 +        if (inputrecNeedMutot(inputrec))
          {
              /* Calculate total (local) dipole moment in a temporary common array.
               * This makes it possible to sum them over nodes faster.
      nbnxn_atomdata_copy_shiftvec(flags & GMX_FORCE_DYNAMICBOX,
                                   fr->shift_vec, nbv->grp[0].nbat);
  
 -#ifdef GMX_MPI
 +#if GMX_MPI
      if (!(cr->duty & DUTY_PME))
      {
          gmx_bool bBS;
          {
              wallcycle_start(wcycle, ewcMOVEX);
              dd_move_x(cr->dd, box, x);
-             /* When we don't need the total dipole we sum it in global_stat */
-             if (bStateChanged && inputrecNeedMutot(inputrec))
-             {
-                 gmx_sumd(2*DIM, mu, cr);
-             }
              wallcycle_stop(wcycle, ewcMOVEX);
  
              wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
          cycles_force += wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
      }
  
 -    if (bStateChanged && NEED_MUTOT(*inputrec))
 +    if (bStateChanged && inputrecNeedMutot(inputrec))
      {
          if (PAR(cr))
          {
      }
  
      /* Reset energies */
 -    reset_enerdata(fr, bNS, enerd, MASTER(cr));
 +    reset_enerdata(enerd);
      clear_rvecs(SHIFTS, fr->fshift);
  
      if (DOMAINDECOMP(cr) && !(cr->duty & DUTY_PME))
  
          /* Clear the short- and long-range forces */
          clear_rvecs(fr->natoms_force_constr, f);
 -        if (bSepLRF && do_per_step(step, inputrec->nstcalclr))
 -        {
 -            clear_rvecs(fr->natoms_force_constr, fr->f_twin);
 -        }
  
          clear_rvec(fr->vir_diag_posres);
      }
      /* Compute the bonded and non-bonded energies and optionally forces */
      do_force_lowlevel(fr, inputrec, &(top->idef),
                        cr, nrnb, wcycle, mdatoms,
 -                      x, hist, f, bSepLRF ? fr->f_twin : f, enerd, fcd, top, fr->born,
 +                      x, hist, f, enerd, fcd, top, fr->born,
                        bBornRadii, box,
                        inputrec->fepvals, lambda, graph, &(top->excls), fr->mu_tot,
                        flags, &cycles_pme);
  
 -    if (bSepLRF)
 -    {
 -        if (do_per_step(step, inputrec->nstcalclr))
 -        {
 -            /* Add the long range forces to the short range forces */
 -            for (i = 0; i < fr->natoms_force_constr; i++)
 -            {
 -                rvec_add(fr->f_twin[i], f[i], f[i]);
 -            }
 -        }
 -    }
 -
      cycles_force += wallcycle_stop(wcycle, ewcFORCE);
  
      if (ed)
  
                  wallcycle_start(wcycle, ewcWAIT_GPU_NB_NL);
                  nbnxn_gpu_wait_for_gpu(nbv->gpu_nbv,
 -                                       nbv->grp[eintNonlocal].nbat,
                                         flags, eatNonlocal,
                                         enerd->grpp.ener[egLJSR], enerd->grpp.ener[egCOULSR],
                                         fr->fshift);
  
      if (bDoForces && DOMAINDECOMP(cr))
      {
 -        if (bUseGPU)
 +        if (bUseGPU && useCuda)
          {
              /* We are done with the CPU compute, but the GPU local non-bonded
               * kernel can still be running while we communicate the forces.
               * We start a counter here, so we can, hopefully, time the rest
               * of the GPU kernel execution and data transfer.
               */
 -            wallcycle_start(wcycle, ewcWAIT_GPU_NB_L_EST);
 +            cycleCountBeforeLocalWorkCompletes = gmx_cycles_read();
          }
  
          /* Communicate the forces */
          wallcycle_start(wcycle, ewcMOVEF);
          dd_move_f(cr->dd, f, fr->fshift);
 -        if (bSepLRF)
 -        {
 -            /* We should not update the shift forces here,
 -             * since f_twin is already included in f.
 -             */
 -            dd_move_f(cr->dd, fr->f_twin, NULL);
 -        }
          wallcycle_stop(wcycle, ewcMOVEF);
      }
  
      if (bUseOrEmulGPU)
      {
          /* wait for local forces (or calculate in emulation mode) */
 -        if (bUseGPU)
 +        if (bUseGPU && useCuda)
          {
 -#if defined(GMX_GPU) && !defined(GMX_USE_OPENCL)
              float       cycles_tmp, cycles_wait_est;
              const float cuda_api_overhead_margin = 50000.0f; /* cycles */
  
              wallcycle_start(wcycle, ewcWAIT_GPU_NB_L);
              nbnxn_gpu_wait_for_gpu(nbv->gpu_nbv,
 -                                   nbv->grp[eintLocal].nbat,
                                     flags, eatLocal,
                                     enerd->grpp.ener[egLJSR], enerd->grpp.ener[egCOULSR],
                                     fr->fshift);
  
              if (bDoForces && DOMAINDECOMP(cr))
              {
 -                cycles_wait_est = wallcycle_stop(wcycle, ewcWAIT_GPU_NB_L_EST);
 +                cycles_wait_est = gmx_cycles_read() - cycleCountBeforeLocalWorkCompletes;
  
                  if (cycles_tmp < cuda_api_overhead_margin)
                  {
               */
              cycles_force    += cycles_wait_est;
              cycles_wait_gpu += cycles_wait_est;
 -
 -#elif defined(GMX_GPU) && defined(GMX_USE_OPENCL)
 +        }
 +        else if (bUseGPU && useOpenCL)
 +        {
  
              wallcycle_start(wcycle, ewcWAIT_GPU_NB_L);
              nbnxn_gpu_wait_for_gpu(nbv->gpu_nbv,
 -                                   nbv->grp[eintLocal].nbat,
                                     flags, eatLocal,
                                     enerd->grpp.ener[egLJSR], enerd->grpp.ener[egCOULSR],
                                     fr->fshift);
              cycles_wait_gpu += wallcycle_stop(wcycle, ewcWAIT_GPU_NB_L);
 -#endif
 -
 +        }
 +        if (bUseGPU)
 +        {
              /* now clear the GPU outputs while we finish the step on the CPU */
              wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU_NB);
              nbnxn_gpu_clear_outputs(nbv->gpu_nbv, flags);
  
      if (bDoForces)
      {
 -        if (IR_ELEC_FIELD(*inputrec))
 +        if (inputrecElecField(inputrec))
          {
              /* Compute forces due to electric field */
              calc_f_el(MASTER(cr) ? field : NULL,
              spread_vsite_f(vsite, x, f, fr->fshift, FALSE, NULL, nrnb,
                             &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
              wallcycle_stop(wcycle, ewcVSITESPREAD);
 -
 -            if (bSepLRF)
 -            {
 -                wallcycle_start(wcycle, ewcVSITESPREAD);
 -                spread_vsite_f(vsite, x, fr->f_twin, NULL, FALSE, NULL,
 -                               nrnb,
 -                               &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 -                wallcycle_stop(wcycle, ewcVSITESPREAD);
 -            }
          }
  
          if (flags & GMX_FORCE_VIRIAL)
@@@ -1511,7 -1520,9 +1505,7 @@@ void do_force_cutsGROUP(FILE *fplog, t_
      int        start, homenr;
      double     mu[2*DIM];
      gmx_bool   bStateChanged, bNS, bFillGrid, bCalcCGCM;
 -    gmx_bool   bDoLongRangeNS, bDoForces, bSepLRF;
 -    gmx_bool   bDoAdressWF;
 -    t_pbc      pbc;
 +    gmx_bool   bDoForces;
      float      cycles_pme, cycles_force;
  
      start  = 0;
  
      bStateChanged  = (flags & GMX_FORCE_STATECHANGED);
      bNS            = (flags & GMX_FORCE_NS) && (fr->bAllvsAll == FALSE);
 -    /* Should we update the long-range neighborlists at this step? */
 -    bDoLongRangeNS = fr->bTwinRange && bNS;
      /* Should we perform the long-range nonbonded evaluation inside the neighborsearching? */
      bFillGrid      = (bNS && bStateChanged);
      bCalcCGCM      = (bFillGrid && !DOMAINDECOMP(cr));
      bDoForces      = (flags & GMX_FORCE_FORCES);
 -    bSepLRF        = ((inputrec->nstcalclr > 1) && bDoForces &&
 -                      (flags & GMX_FORCE_SEPLRF) && (flags & GMX_FORCE_DO_LR));
 -
 -    /* should probably move this to the forcerec since it doesn't change */
 -    bDoAdressWF   = ((fr->adress_type != eAdressOff));
  
      if (bStateChanged)
      {
          update_forcerec(fr, box);
  
 -        if (NEED_MUTOT(*inputrec))
 +        if (inputrecNeedMutot(inputrec))
          {
              /* Calculate total (local) dipole moment in a temporary common array.
               * This makes it possible to sum them over nodes faster.
          pr_rvecs(debug, 0, "cgcm", fr->cg_cm, top->cgs.nr);
      }
  
 -#ifdef GMX_MPI
 +#if GMX_MPI
      if (!(cr->duty & DUTY_PME))
      {
          gmx_bool bBS;
          wallcycle_stop(wcycle, ewcMOVEX);
      }
  
 -    /* update adress weight beforehand */
 -    if (bStateChanged && bDoAdressWF)
 -    {
 -        /* need pbc for adress weight calculation with pbc_dx */
 -        set_pbc(&pbc, inputrec->ePBC, box);
 -        if (fr->adress_site == eAdressSITEcog)
 -        {
 -            update_adress_weights_cog(top->idef.iparams, top->idef.il, x, fr, mdatoms,
 -                                      inputrec->ePBC == epbcNONE ? NULL : &pbc);
 -        }
 -        else if (fr->adress_site == eAdressSITEcom)
 -        {
 -            update_adress_weights_com(fplog, cg0, cg1, &(top->cgs), x, fr, mdatoms,
 -                                      inputrec->ePBC == epbcNONE ? NULL : &pbc);
 -        }
 -        else if (fr->adress_site == eAdressSITEatomatom)
 -        {
 -            update_adress_weights_atom_per_atom(cg0, cg1, &(top->cgs), x, fr, mdatoms,
 -                                                inputrec->ePBC == epbcNONE ? NULL : &pbc);
 -        }
 -        else
 -        {
 -            update_adress_weights_atom(cg0, cg1, &(top->cgs), x, fr, mdatoms,
 -                                       inputrec->ePBC == epbcNONE ? NULL : &pbc);
 -        }
 -    }
 -
 -    if (NEED_MUTOT(*inputrec))
 +    if (inputrecNeedMutot(inputrec))
      {
 -
          if (bStateChanged)
          {
              if (PAR(cr))
      }
  
      /* Reset energies */
 -    reset_enerdata(fr, bNS, enerd, MASTER(cr));
 +    reset_enerdata(enerd);
      clear_rvecs(SHIFTS, fr->fshift);
  
      if (bNS)
          /* Do the actual neighbour searching */
          ns(fplog, fr, box,
             groups, top, mdatoms,
 -           cr, nrnb, bFillGrid,
 -           bDoLongRangeNS);
 +           cr, nrnb, bFillGrid);
  
          wallcycle_stop(wcycle, ewcNS);
      }
  
          /* Clear the short- and long-range forces */
          clear_rvecs(fr->natoms_force_constr, f);
 -        if (bSepLRF && do_per_step(step, inputrec->nstcalclr))
 -        {
 -            clear_rvecs(fr->natoms_force_constr, fr->f_twin);
 -        }
  
          clear_rvec(fr->vir_diag_posres);
      }
      /* Compute the bonded and non-bonded energies and optionally forces */
      do_force_lowlevel(fr, inputrec, &(top->idef),
                        cr, nrnb, wcycle, mdatoms,
 -                      x, hist, f, bSepLRF ? fr->f_twin : f, enerd, fcd, top, fr->born,
 +                      x, hist, f, enerd, fcd, top, fr->born,
                        bBornRadii, box,
                        inputrec->fepvals, lambda,
                        graph, &(top->excls), fr->mu_tot,
                        flags,
                        &cycles_pme);
  
 -    if (bSepLRF)
 -    {
 -        if (do_per_step(step, inputrec->nstcalclr))
 -        {
 -            /* Add the long range forces to the short range forces */
 -            for (i = 0; i < fr->natoms_force_constr; i++)
 -            {
 -                rvec_add(fr->f_twin[i], f[i], f[i]);
 -            }
 -        }
 -    }
 -
      cycles_force = wallcycle_stop(wcycle, ewcFORCE);
  
      if (ed)
  
      if (bDoForces)
      {
 -        if (IR_ELEC_FIELD(*inputrec))
 +        if (inputrecElecField(inputrec))
          {
              /* Compute forces due to electric field */
              calc_f_el(MASTER(cr) ? field : NULL,
                        inputrec->ex, inputrec->et, t);
          }
  
 -        if (bDoAdressWF && fr->adress_icor == eAdressICThermoForce)
 -        {
 -            /* Compute thermodynamic force in hybrid AdResS region */
 -            adress_thermo_force(start, homenr, &(top->cgs), x, fr->f_novirsum, fr, mdatoms,
 -                                inputrec->ePBC == epbcNONE ? NULL : &pbc);
 -        }
 -
          /* Communicate the forces */
          if (DOMAINDECOMP(cr))
          {
              {
                  dd_move_f(cr->dd, fr->f_novirsum, NULL);
              }
 -            if (bSepLRF)
 -            {
 -                /* We should not update the shift forces here,
 -                 * since f_twin is already included in f.
 -                 */
 -                dd_move_f(cr->dd, fr->f_twin, NULL);
 -            }
              wallcycle_stop(wcycle, ewcMOVEF);
          }
  
              spread_vsite_f(vsite, x, f, fr->fshift, FALSE, NULL, nrnb,
                             &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
              wallcycle_stop(wcycle, ewcVSITESPREAD);
 -
 -            if (bSepLRF)
 -            {
 -                wallcycle_start(wcycle, ewcVSITESPREAD);
 -                spread_vsite_f(vsite, x, fr->f_twin, NULL, FALSE, NULL,
 -                               nrnb,
 -                               &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 -                wallcycle_stop(wcycle, ewcVSITESPREAD);
 -            }
          }
  
          if (flags & GMX_FORCE_VIRIAL)
@@@ -1948,10 -2034,7 +1942,10 @@@ void do_constrain_first(FILE *fplog, gm
      real            dvdl_dum;
      rvec           *savex;
  
 -    snew(savex, state->natoms);
 +    /* We need to allocate one element extra, since we might use
 +     * (unaligned) 4-wide SIMD loads to access rvec entries.
 +     */
 +    snew(savex, state->natoms + 1);
  
      start = 0;
      end   = md->homenr;
@@@ -2136,13 -2219,8 +2130,13 @@@ void calc_enervirdiff(FILE *fplog, int 
                            "for vdw-type = %s", evdw_names[fr->vdwtype]);
              }
  
 -            scale  = fr->nblists[0].table_vdw.scale;
 -            vdwtab = fr->nblists[0].table_vdw.data;
 +            /* TODO This code depends on the logic in tables.c that
 +               constructs the table layout, which should be made
 +               explicit in future cleanup. */
 +            GMX_ASSERT(fr->dispersionCorrectionTable->interaction == GMX_TABLE_INTERACTION_VDWREP_VDWDISP,
 +                       "Dispersion-correction code needs a table with both repulsion and dispersion terms");
 +            scale  = fr->dispersionCorrectionTable->scale;
 +            vdwtab = fr->dispersionCorrectionTable->data;
  
              /* Round the cut-offs to exact table values for precision */
              ri0  = static_cast<int>(floor(fr->rvdw_switch*scale));
               * the reciprocal-space contribution is constant in the
               * region that contributes to the self-interaction).
               */
 -            fr->enershiftsix = pow(fr->ewaldcoeff_lj, 6) / 6.0;
 +            fr->enershiftsix = gmx::power6(fr->ewaldcoeff_lj) / 6.0;
  
 -            eners[0] += -pow(sqrt(M_PI)*fr->ewaldcoeff_lj, 3)/3.0;
 -            virs[0]  +=  pow(sqrt(M_PI)*fr->ewaldcoeff_lj, 3);
 +            eners[0] += -gmx::power3(std::sqrt(M_PI)*fr->ewaldcoeff_lj)/3.0;
 +            virs[0]  +=  gmx::power3(std::sqrt(M_PI)*fr->ewaldcoeff_lj);
          }
  
          fr->enerdiffsix    = eners[0];
  }
  
  void calc_dispcorr(t_inputrec *ir, t_forcerec *fr,
 -                   int natoms,
                     matrix box, real lambda, tensor pres, tensor virial,
                     real *prescorr, real *enercorr, real *dvdlcorr)
  {
          if (fr->n_tpi)
          {
              /* Only correct for the interactions with the inserted molecule */
 -            dens   = (natoms - fr->n_tpi)*invvol;
 +            dens   = (fr->numAtomsForDispersionCorrection - fr->n_tpi)*invvol;
              ninter = fr->n_tpi;
          }
          else
          {
 -            dens   = natoms*invvol;
 -            ninter = 0.5*natoms;
 +            dens   = fr->numAtomsForDispersionCorrection*invvol;
 +            ninter = 0.5*fr->numAtomsForDispersionCorrection;
          }
  
          if (ir->efep == efepNO)
@@@ -2422,7 -2501,7 +2416,7 @@@ void do_pbc_first(FILE *fplog, matrix b
  }
  
  static void low_do_pbc_mtop(FILE *fplog, int ePBC, matrix box,
 -                            gmx_mtop_t *mtop, rvec x[],
 +                            const gmx_mtop_t *mtop, rvec x[],
                              gmx_bool bFirst)
  {
      t_graph        *graph;
  }
  
  void do_pbc_first_mtop(FILE *fplog, int ePBC, matrix box,
 -                       gmx_mtop_t *mtop, rvec x[])
 +                       const gmx_mtop_t *mtop, rvec x[])
  {
      low_do_pbc_mtop(fplog, ePBC, box, mtop, x, TRUE);
  }
@@@ -2481,27 -2560,6 +2475,27 @@@ void do_pbc_mtop(FILE *fplog, int ePBC
      low_do_pbc_mtop(fplog, ePBC, box, mtop, x, FALSE);
  }
  
 +void put_atoms_in_box_omp(int ePBC, const matrix box, int natoms, rvec x[])
 +{
 +    int t, nth;
 +    nth = gmx_omp_nthreads_get(emntDefault);
 +
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +    for (t = 0; t < nth; t++)
 +    {
 +        try
 +        {
 +            int offset, len;
 +
 +            offset = (natoms*t    )/nth;
 +            len    = (natoms*(t + 1))/nth - offset;
 +            put_atoms_in_box(ePBC, box, len, x + offset);
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +}
 +
 +// TODO This can be cleaned up a lot, and move back to runner.cpp
  void finish_run(FILE *fplog, t_commrec *cr,
                  t_inputrec *inputrec,
                  t_nrnb nrnb[], gmx_wallcycle_t wcycle,
              elapsed_time_over_all_ranks,
              elapsed_time_over_all_threads,
              elapsed_time_over_all_threads_over_all_ranks;
 -    wallcycle_sum(cr, wcycle);
  
      if (cr->nnodes > 1)
      {
          snew(nrnb_tot, 1);
 -#ifdef GMX_MPI
 +#if GMX_MPI
          MPI_Allreduce(nrnb->n, nrnb_tot->n, eNRNB, MPI_DOUBLE, MPI_SUM,
                        cr->mpi_comm_mysim);
  #endif
      elapsed_time_over_all_ranks                  = elapsed_time;
      elapsed_time_over_all_threads                = walltime_accounting_get_elapsed_time_over_all_threads(walltime_accounting);
      elapsed_time_over_all_threads_over_all_ranks = elapsed_time_over_all_threads;
 -#ifdef GMX_MPI
 +#if GMX_MPI
      if (cr->nnodes > 1)
      {
          /* reduce elapsed_time over all MPI ranks in the current simulation */
          print_dd_statistics(cr, inputrec, fplog);
      }
  
 +    /* TODO Move the responsibility for any scaling by thread counts
 +     * to the code that handled the thread region, so that there's a
 +     * mechanism to keep cycle counting working during the transition
 +     * to task parallelism. */
 +    int nthreads_pp  = gmx_omp_nthreads_get(emntNonbonded);
 +    int nthreads_pme = gmx_omp_nthreads_get(emntPME);
 +    wallcycle_scale_by_num_threads(wcycle, cr->duty == DUTY_PME, nthreads_pp, nthreads_pme);
 +    auto cycle_sum(wallcycle_sum(cr, wcycle));
 +
      if (SIMMASTER(cr))
      {
          struct gmx_wallclock_gpu_t* gputimes = use_GPU(nbv) ? nbnxn_gpu_get_timings(nbv->gpu_nbv) : NULL;
  
 -        wallcycle_print(fplog, cr->nnodes, cr->npmenodes,
 +        wallcycle_print(fplog, cr->nnodes, cr->npmenodes, nthreads_pp, nthreads_pme,
                          elapsed_time_over_all_ranks,
 -                        wcycle, gputimes);
 +                        wcycle, cycle_sum, gputimes);
  
          if (EI_DYNAMICS(inputrec->eI))
          {
@@@ -2678,11 -2728,11 +2672,11 @@@ extern void initialize_lambdas(FILE *fp
  
  
  void init_md(FILE *fplog,
 -             t_commrec *cr, t_inputrec *ir, const output_env_t oenv,
 +             t_commrec *cr, t_inputrec *ir, const gmx_output_env_t *oenv,
               double *t, double *t0,
               real *lambda, int *fep_state, double *lam0,
               t_nrnb *nrnb, gmx_mtop_t *mtop,
 -             gmx_update_t *upd,
 +             gmx_update_t **upd,
               int nfile, const t_filenm fnm[],
               gmx_mdoutf_t *outf, t_mdebin **mdebin,
               tensor force_vir, tensor shake_vir, rvec mu_tot,
              *bSimAnn = TRUE;
          }
      }
 -    if (*bSimAnn)
 -    {
 -        update_annealing_target_temp(&(ir->opts), ir->init_t);
 -    }
  
      /* Initialize lambda variables */
      initialize_lambdas(fplog, ir, fep_state, lambda, lam0);
  
 +    // TODO upd is never NULL in practice, but the analysers don't know that
      if (upd)
      {
          *upd = init_update(ir);
      }
 -
 +    if (*bSimAnn)
 +    {
 +        update_annealing_target_temp(ir, ir->init_t, upd ? *upd : NULL);
 +    }
  
      if (vcm != NULL)
      {
                                mtop, ir, mdoutf_get_fp_dhdl(*outf));
      }
  
 -    if (ir->bAdress)
 -    {
 -        please_cite(fplog, "Fritsch12");
 -        please_cite(fplog, "Junghans10");
 -    }
      /* Initiate variables */
      clear_mat(force_vir);
      clear_mat(shake_vir);
      clear_rvec(mu_tot);
 -
 -    debug_gmx();
  }
index 27cb72959eaad916fab680ba829b6486a767cd82,0000000000000000000000000000000000000000..951c9890f7ad85fe2a7a4a70504ddc4e30ce0922
mode 100644,000000..100644
--- /dev/null
@@@ -1,338 -1,0 +1,338 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team.
-                 /* nbfp now includes the 6.0/12.0 derivative prefactors */
-                 Cd = nbfp[ntw[w]+2*at]/6.0;
-                 Cr = nbfp[ntw[w]+2*at+1]/12.0;
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#include "gmxpre.h"
 +
 +#include <string.h>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fileio/filetypes.h"
 +#include "gromacs/gmxlib/nrnb.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/force.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/mdatom.h"
 +#include "gromacs/mdtypes/nblist.h"
 +#include "gromacs/tables/forcetable.h"
 +#include "gromacs/topology/topology.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +void make_wall_tables(FILE *fplog,
 +                      const t_inputrec *ir, const char *tabfn,
 +                      const gmx_groups_t *groups,
 +                      t_forcerec *fr)
 +{
 +    int           negp_pp;
 +    int          *nm_ind;
 +    char          buf[STRLEN];
 +
 +    negp_pp = ir->opts.ngener - ir->nwall;
 +    nm_ind  = groups->grps[egcENER].nm_ind;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Reading user tables for %d energy groups with %d walls\n",
 +                negp_pp, ir->nwall);
 +    }
 +
 +    snew(fr->wall_tab, ir->nwall);
 +    for (int w = 0; w < ir->nwall; w++)
 +    {
 +        snew(fr->wall_tab[w], negp_pp);
 +        for (int egp = 0; egp < negp_pp; egp++)
 +        {
 +            /* If the energy group pair is excluded, we don't need a table */
 +            if (!(fr->egp_flags[egp*ir->opts.ngener+negp_pp+w] & EGP_EXCL))
 +            {
 +                fr->wall_tab[w][egp] = make_tables(fplog, fr, buf, 0,
 +                                                   GMX_MAKETABLES_FORCEUSER);
 +                sprintf(buf, "%s", tabfn);
 +                sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s",
 +                        *groups->grpname[nm_ind[egp]],
 +                        *groups->grpname[nm_ind[negp_pp+w]],
 +                        ftp2ext(efXVG));
 +
 +                /* Since wall have no charge, we can compress the table */
 +                for (int i = 0; i <= fr->wall_tab[w][egp]->n; i++)
 +                {
 +                    for (int j = 0; j < 8; j++)
 +                    {
 +                        fr->wall_tab[w][egp]->data[8*i+j] =
 +                            fr->wall_tab[w][egp]->data[12*i+4+j];
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void wall_error(int a, rvec *x, real r)
 +{
 +    gmx_fatal(FARGS,
 +              "An atom is beyond the wall: coordinates %f %f %f, distance %f\n"
 +              "You might want to use the mdp option wall_r_linpot",
 +              x[a][XX], x[a][YY], x[a][ZZ], r);
 +}
 +
 +real do_walls(t_inputrec *ir, t_forcerec *fr, matrix box, t_mdatoms *md,
 +              rvec x[], rvec f[], real lambda, real Vlj[], t_nrnb *nrnb)
 +{
 +    int             nwall;
 +    int             ntw[2], at, ntype, ngid, ggid, *egp_flags, *type;
 +    real           *nbfp, lamfac, fac_d[2], fac_r[2], Cd, Cr, Vtot;
 +    real            wall_z[2], r, mr, r1, r2, r4, Vd, Vr, V = 0, Fd, Fr, F = 0, dvdlambda;
 +    dvec            xf_z;
 +    int             n0, nnn;
 +    real            tabscale, *VFtab, rt, eps, eps2, Yt, Ft, Geps, Heps2, Fp, VV, FF;
 +    unsigned short *gid = md->cENER;
 +    t_forcetable   *tab;
 +
 +    nwall     = ir->nwall;
 +    ngid      = ir->opts.ngener;
 +    ntype     = fr->ntype;
 +    nbfp      = fr->nbfp;
 +    egp_flags = fr->egp_flags;
 +
 +    for (int w = 0; w < nwall; w++)
 +    {
 +        ntw[w] = 2*ntype*ir->wall_atomtype[w];
 +        switch (ir->wall_type)
 +        {
 +            case ewt93:
 +                fac_d[w] = ir->wall_density[w]*M_PI/6;
 +                fac_r[w] = ir->wall_density[w]*M_PI/45;
 +                break;
 +            case ewt104:
 +                fac_d[w] = ir->wall_density[w]*M_PI/2;
 +                fac_r[w] = ir->wall_density[w]*M_PI/5;
 +                break;
 +            default:
 +                break;
 +        }
 +    }
 +    wall_z[0] = 0;
 +    wall_z[1] = box[ZZ][ZZ];
 +
 +    Vtot      = 0;
 +    dvdlambda = 0;
 +    clear_dvec(xf_z);
 +    for (int lam = 0; lam < (md->nPerturbed ? 2 : 1); lam++)
 +    {
 +        if (md->nPerturbed)
 +        {
 +            if (lam == 0)
 +            {
 +                lamfac = 1 - lambda;
 +                type   = md->typeA;
 +            }
 +            else
 +            {
 +                lamfac = lambda;
 +                type   = md->typeB;
 +            }
 +        }
 +        else
 +        {
 +            lamfac = 1;
 +            type   = md->typeA;
 +        }
 +        for (int i = 0; i < md->homenr; i++)
 +        {
 +            for (int w = 0; w < std::min(nwall, 2); w++)
 +            {
 +                /* The wall energy groups are always at the end of the list */
 +                ggid = gid[i]*ngid + ngid - nwall + w;
 +                at   = type[i];
-                                 Vd    = Cd*VV;
-                                 Fd    = Cd*FF;
++                /* nbfp now includes the 6/12 derivative prefactors */
++                Cd = nbfp[ntw[w]+2*at]/6;
++                Cr = nbfp[ntw[w]+2*at+1]/12;
 +                if (!((Cd == 0 && Cr == 0) || (egp_flags[ggid] & EGP_EXCL)))
 +                {
 +                    if (w == 0)
 +                    {
 +                        r = x[i][ZZ];
 +                    }
 +                    else
 +                    {
 +                        r = wall_z[1] - x[i][ZZ];
 +                    }
 +                    if (r < ir->wall_r_linpot)
 +                    {
 +                        mr = ir->wall_r_linpot - r;
 +                        r  = ir->wall_r_linpot;
 +                    }
 +                    else
 +                    {
 +                        mr = 0;
 +                    }
 +                    switch (ir->wall_type)
 +                    {
 +                        case ewtTABLE:
 +                            if (r < 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            tab      = fr->wall_tab[w][gid[i]];
 +                            tabscale = tab->scale;
 +                            VFtab    = tab->data;
 +
 +                            rt    = r*tabscale;
 +                            n0    = static_cast<int>(rt);
 +                            if (n0 >= tab->n)
 +                            {
 +                                /* Beyond the table range, set V and F to zero */
 +                                V     = 0;
 +                                F     = 0;
 +                            }
 +                            else
 +                            {
 +                                eps   = rt - n0;
 +                                eps2  = eps*eps;
 +                                /* Dispersion */
 +                                nnn   = 8*n0;
 +                                Yt    = VFtab[nnn];
 +                                Ft    = VFtab[nnn+1];
 +                                Geps  = VFtab[nnn+2]*eps;
 +                                Heps2 = VFtab[nnn+3]*eps2;
 +                                Fp    = Ft + Geps + Heps2;
 +                                VV    = Yt + Fp*eps;
 +                                FF    = Fp + Geps + 2.0*Heps2;
-                                 Vr    = Cr*VV;
-                                 Fr    = Cr*FF;
++                                Vd    = 6*Cd*VV;
++                                Fd    = 6*Cd*FF;
 +                                /* Repulsion */
 +                                nnn   = nnn + 4;
 +                                Yt    = VFtab[nnn];
 +                                Ft    = VFtab[nnn+1];
 +                                Geps  = VFtab[nnn+2]*eps;
 +                                Heps2 = VFtab[nnn+3]*eps2;
 +                                Fp    = Ft + Geps + Heps2;
 +                                VV    = Yt + Fp*eps;
 +                                FF    = Fp + Geps + 2.0*Heps2;
++                                Vr    = 12*Cr*VV;
++                                Fr    = 12*Cr*FF;
 +                                V     = Vd + Vr;
 +                                F     = -lamfac*(Fd + Fr)*tabscale;
 +                            }
 +                            break;
 +                        case ewt93:
 +                            if (r <= 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            r1 = 1/r;
 +                            r2 = r1*r1;
 +                            r4 = r2*r2;
 +                            Vd = fac_d[w]*Cd*r2*r1;
 +                            Vr = fac_r[w]*Cr*r4*r4*r1;
 +                            V  = Vr - Vd;
 +                            F  = lamfac*(9*Vr - 3*Vd)*r1;
 +                            break;
 +                        case ewt104:
 +                            if (r <= 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            r1 = 1/r;
 +                            r2 = r1*r1;
 +                            r4 = r2*r2;
 +                            Vd = fac_d[w]*Cd*r4;
 +                            Vr = fac_r[w]*Cr*r4*r4*r2;
 +                            V  = Vr - Vd;
 +                            F  = lamfac*(10*Vr - 4*Vd)*r1;
 +                            break;
 +                        case ewt126:
 +                            if (r <= 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            r1 = 1/r;
 +                            r2 = r1*r1;
 +                            r4 = r2*r2;
 +                            Vd = Cd*r4*r2;
 +                            Vr = Cr*r4*r4*r4;
 +                            V  = Vr - Vd;
 +                            F  = lamfac*(12*Vr - 6*Vd)*r1;
 +                            break;
 +                        default:
 +                            break;
 +                    }
 +                    if (mr > 0)
 +                    {
 +                        V += mr*F;
 +                    }
 +                    if (w == 1)
 +                    {
 +                        F = -F;
 +                    }
 +                    Vlj[ggid] += lamfac*V;
 +                    Vtot      += V;
 +                    f[i][ZZ]  += F;
 +                    /* Because of the single sum virial calculation we need
 +                     * to add  the full virial contribution of the walls.
 +                     * Since the force only has a z-component, there is only
 +                     * a contribution to the z component of the virial tensor.
 +                     * We could also determine the virial contribution directly,
 +                     * which would be cheaper here, but that would require extra
 +                     * communication for f_novirsum for with virtual sites
 +                     * in parallel.
 +                     */
 +                    xf_z[XX]  -= x[i][XX]*F;
 +                    xf_z[YY]  -= x[i][YY]*F;
 +                    xf_z[ZZ]  -= wall_z[w]*F;
 +                }
 +            }
 +        }
 +        if (md->nPerturbed)
 +        {
 +            dvdlambda += (lam == 0 ? -1 : 1)*Vtot;
 +        }
 +
 +        inc_nrnb(nrnb, eNR_WALLS, md->homenr);
 +    }
 +
 +    for (int i = 0; i < DIM; i++)
 +    {
 +        fr->vir_wall_z[i] = -0.5*xf_z[i];
 +    }
 +
 +    return dvdlambda;
 +}
index acbeff92019d915c5c2cbe784aeac3a01d55d78a,c29efb4238cddea08cc737130c4165c3729e34f1..315dc7dd5077753f4c1f64fccb434e227ae86067
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
-  * Copyright (c) 2014,2015, by the GROMACS development team, led by
+  * Copyright (c) 2014,2015,2016, 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.
  #ifndef GMX_SIMD_IMPLEMENTATION_IBM_VMX_H
  #define GMX_SIMD_IMPLEMENTATION_IBM_VMX_H
  
 -#include <math.h>
 -
 -#include <altivec.h>
 -
 -/* Make sure we do not screw up c++ - undefine vector/bool, and rely on __vector and __bool */
 -#undef vector
 -#undef bool
 -
 -/* IBM VMX SIMD instruction wrappers. Power6 and later.
 - *
 - * Please see documentation in gromacs/simd/simd.h for the available
 - * defines.
 - */
 -/* Capability definitions for IBM VMX */
 -#define GMX_SIMD_HAVE_FLOAT
 -#undef  GMX_SIMD_HAVE_DOUBLE
 -#define GMX_SIMD_HAVE_HARDWARE
 -#undef  GMX_SIMD_HAVE_LOADU
 -#undef  GMX_SIMD_HAVE_STOREU
 -#define GMX_SIMD_HAVE_LOGICAL
 -/* VMX only provides fmadd/fnmadd (our definitions), but not fmsub/fnmsub.
 - * However, fnmadd is what we need for 1/sqrt(x).
 - */
 -#define GMX_SIMD_HAVE_FMA
 -#undef  GMX_SIMD_HAVE_FRACTION
 -#define GMX_SIMD_HAVE_FINT32
 -#undef  GMX_SIMD_HAVE_FINT32_EXTRACT
 -#define GMX_SIMD_HAVE_FINT32_LOGICAL
 -#define GMX_SIMD_HAVE_FINT32_ARITHMETICS
 -#undef  GMX_SIMD_HAVE_DINT32
 -#undef  GMX_SIMD_HAVE_DINT32_EXTRACT
 -#undef  GMX_SIMD_HAVE_DINT32_LOGICAL
 -#undef  GMX_SIMD_HAVE_DINT32_ARITHMETICS
 -#define GMX_SIMD4_HAVE_FLOAT
 -#undef  GMX_SIMD4_HAVE_DOUBLE
 -
 -/* Implementation details */
 -#define GMX_SIMD_FLOAT_WIDTH         4
 -#undef  GMX_SIMD_DOUBLE_WIDTH
 -#define GMX_SIMD_FINT32_WIDTH        4
 -#undef  GMX_SIMD_DINT32_WIDTH
 -#define GMX_SIMD_RSQRT_BITS         14
 -#define GMX_SIMD_RCP_BITS           14
 -
 -/****************************************************
 - *      SINGLE PRECISION SIMD IMPLEMENTATION        *
 - ****************************************************/
 -#define gmx_simd_float_t           __vector float
 -#define gmx_simd_load_f(m)         vec_ld(0, (const __vector float *)(m))
 -#define gmx_simd_load1_f(m)        gmx_simd_load1_f_ibm_vmx(m)
 -#define gmx_simd_set1_f(x)         gmx_simd_set1_f_ibm_vmx(x)
 -#define gmx_simd_store_f(m, x)     vec_st(x, 0, (__vector float *)(m))
 -#undef  gmx_simd_loadu_f
 -#undef  gmx_simd_storeu_f
 -#define gmx_simd_setzero_f()       ((__vector float)vec_splat_u32(0))
 -#define gmx_simd_add_f(a, b)       vec_add(a, b)
 -#define gmx_simd_sub_f(a, b)       vec_sub(a, b)
 -#define gmx_simd_mul_f(a, b)       vec_madd(a, b, ((__vector float)vec_splat_u32(0)))
 -#define gmx_simd_fmadd_f(a, b, c)  vec_madd(a, b, c)
 -#define gmx_simd_fmsub_f(a, b, c)  vec_madd(a, b, -c)
 -/* IBM uses an alternative FMA definition, so -a*b+c=-(a*b-c) is "nmsub" */
 -#define gmx_simd_fnmadd_f(a, b, c) vec_nmsub(a, b, c)
 -/* IBM uses an alternative FMA definition, so -a*b-c=-(a*b+c) is "nmadd" */
 -#define gmx_simd_fnmsub_f(a, b, c) (-vec_madd(a, b, c))
 -#define gmx_simd_and_f(a, b)       vec_and(a, b)
 -#define gmx_simd_andnot_f(a, b)    vec_andc(b, a)
 -#define gmx_simd_or_f(a, b)        vec_or(a, b)
 -#define gmx_simd_xor_f(a, b)       vec_xor(a, b)
 -#define gmx_simd_rsqrt_f(a)        vec_rsqrte(a)
 -#define gmx_simd_rcp_f(a)          vec_re(a)
 -#define gmx_simd_fabs_f(a)         vec_abs(a)
 -#define gmx_simd_fneg_f(a)         vec_xor(a, (__vector float)vec_sl(vec_splat_u32(-1), vec_splat_u32(-1)))
 -#define gmx_simd_max_f(a, b)       vec_max(a, b)
 -#define gmx_simd_min_f(a, b)       vec_min(a, b)
 -#define gmx_simd_round_f(a)        vec_round(a)
 -#define gmx_simd_trunc_f(a)        vec_trunc(a)
 -#define gmx_simd_fraction_f(x)     vec_sub(x, vec_trunc(x))
 -#define gmx_simd_get_exponent_f(a) gmx_simd_get_exponent_f_ibm_vmx(a)
 -#define gmx_simd_get_mantissa_f(a) gmx_simd_get_mantissa_f_ibm_vmx(a)
 -#define gmx_simd_set_exponent_f(a) gmx_simd_set_exponent_f_ibm_vmx(a)
 -/* integer datatype corresponding to float: gmx_simd_fint32_t */
 -#define gmx_simd_fint32_t          __vector int
 -#define gmx_simd_load_fi(m)        vec_ld(0, (const __vector int *)(m))
 -#define gmx_simd_set1_fi(i)        gmx_simd_set1_fi_ibm_vmx((int)(i))
 -#define gmx_simd_store_fi(m, x)    vec_st(x, 0, (__vector int *)(m))
 -#undef  gmx_simd_loadu_fi
 -#undef  gmx_simd_storeu_fi
 -#define gmx_simd_setzero_fi()      vec_splat_s32(0)
 -#define gmx_simd_cvt_f2i(a)        vec_cts(vec_round(a), 0)
 -#define gmx_simd_cvtt_f2i(a)       vec_cts(a, 0)
 -#define gmx_simd_cvt_i2f(a)        vec_ctf(a, 0)
 -#undef  gmx_simd_extract_fi
 -/* Integer logical ops on gmx_simd_fint32_t */
 -/* The shift constant magic requires an explanation:
 - * VMX only allows literals up to 15 to be created directly with vec_splat_u32,
 - * and we need to be able to shift up to 31 bits. The code on the right hand
 - * side splits the constant in three parts with values in the range 0..15.
 - * Since the argument has to be a constant (but our and VMX requirement), these
 - * constants will be evaluated at compile-time, and if one or two parts evaluate
 - * to zero they will be removed with -O2 or higher optimization (checked).
 - */
 -#define gmx_simd_slli_fi(a, i)      vec_sl(a, vec_add(vec_add(vec_splat_u32( (((i&0xF)+(i/16))&0xF)+i/31 ), vec_splat_u32( (i/16)*15 )), vec_splat_u32( (i/31)*15 )))
 -#define gmx_simd_srli_fi(a, i)      vec_sr(a, vec_add(vec_add(vec_splat_u32( (((i&0xF)+(i/16))&0xF)+i/31 ), vec_splat_u32( (i/16)*15 )), vec_splat_u32( (i/31)*15 )))
 -#define gmx_simd_and_fi(a, b)       vec_and(a, b)
 -#define gmx_simd_andnot_fi(a, b)   vec_andc(b, a)
 -#define gmx_simd_or_fi(a, b)        vec_or(a, b)
 -#define gmx_simd_xor_fi(a, b)       vec_xor(a, b)
 -/* Integer arithmetic ops on gmx_simd_fint32_t */
 -#define gmx_simd_add_fi(a, b)       vec_add(a, b)
 -#define gmx_simd_sub_fi(a, b)       vec_sub(a, b)
 -#define gmx_simd_mul_fi(a, b)       vec_mule((__vector short)a, (__vector short)b)
 -/* Boolean & comparison operations on gmx_simd_float_t */
 -#define gmx_simd_fbool_t           __vector __bool int
 -#define gmx_simd_cmpeq_f(a, b)     vec_cmpeq(a, b)
 -#define gmx_simd_cmplt_f(a, b)     vec_cmplt(a, b)
 -#define gmx_simd_cmple_f(a, b)     vec_cmple(a, b)
 -#define gmx_simd_and_fb(a, b)      vec_and(a, b)
 -#define gmx_simd_or_fb(a, b)       vec_or(a, b)
 -#define gmx_simd_anytrue_fb(a)     vec_any_ne(a, (__vector __bool int)vec_splat_u32(0))
 -#define gmx_simd_blendzero_f(a, sel)    vec_and(a, (__vector float)sel)
 -#define gmx_simd_blendnotzero_f(a, sel) vec_andc(a, (__vector float)sel)
 -#define gmx_simd_blendv_f(a, b, sel)    vec_sel(a, b, sel)
 -#define gmx_simd_reduce_f(a)       gmx_simd_reduce_f_ibm_vmx(a)
 -/* Boolean & comparison operations on gmx_simd_fint32_t */
 -#define gmx_simd_fibool_t          __vector __bool int
 -#define gmx_simd_cmpeq_fi(a, b)     vec_cmpeq(a, b)
 -#define gmx_simd_cmplt_fi(a, b)     vec_cmplt(a, b)
 -#define gmx_simd_and_fib(a, b)      vec_and(a, b)
 -#define gmx_simd_or_fib(a, b)       vec_or(a, b)
 -#define gmx_simd_anytrue_fib(a)          vec_any_ne(a, (__vector __bool int)vec_splat_u32(0))
 -#define gmx_simd_blendzero_fi(a, sel)    vec_and(a, (__vector int)sel)
 -#define gmx_simd_blendnotzero_fi(a, sel) vec_andc(a, (__vector int)sel)
 -#define gmx_simd_blendv_fi(a, b, sel)    vec_sel(a, b, sel)
 -/* Conversions between different booleans */
 -#define gmx_simd_cvt_fb2fib(x)     (x)
 -#define gmx_simd_cvt_fib2fb(x)     (x)
 -
 -/* Double is not available with VMX SIMD */
 -
 -/****************************************************
 - * IMPLEMENTATION HELPER FUNCTIONS                  *
 - ****************************************************/
 -static gmx_inline gmx_simd_float_t
 -gmx_simd_set1_f_ibm_vmx(const float x)
 -{
 -    /* In the old days when PPC was all big endian we could
 -     * use the vec_lvsl() instruction to permute bytes based on
 -     * a load adress. However, at least with gcc-4.8.2 the bytes
 -     * end up reversed on Power8 running little endian (Linux).
 -     * Since this is not a critical instruction we work around
 -     * it by first putting the data in an aligned position before
 -     * loading, so we can avoid vec_lvsl() entirely. We can
 -     * do this slightly faster on GCC with alignment attributes.
 -     */
 -    __vector float vx;
 -#ifdef __GNUC__
 -    float alignedx __attribute ((aligned (16)));
 -    alignedx = x;
 -    vx       = vec_lde(0, &alignedx);
 -#else
 -    struct {
 -        vector float vx; float x[4];
 -    } conv;
 -    conv.x[0] = x;
 -    vx        = vec_lde(0, conv.x);
 -#endif
 -    return vec_splat(vx, 0);
 -}
 -
 -static gmx_inline gmx_simd_float_t
 -gmx_simd_load1_f_ibm_vmx(const float * m)
 -{
 -    return gmx_simd_set1_f_ibm_vmx(*m);
 -}
 -
 -static gmx_inline gmx_simd_fint32_t
 -gmx_simd_set1_fi_ibm_vmx(const int x)
 -{
 -    /* See comment in gmx_simd_set1_f_ibm_vmx why we
 -     * cannot use vec_lvsl().
 -     */
 -    __vector int vx;
 -#ifdef __GNUC__
 -    int alignedx __attribute ((aligned (16)));
 -    alignedx = x;
 -    vx       = vec_lde(0, &alignedx);
 -#else
 -    struct {
 -        vector int vx; int x[4];
 -    } conv;
 -    conv.x[0] = x;
 -    vx        = vec_lde(0, conv.x);
 -#endif
 -    return vec_splat(vx, 0);
 -}
 -
 -
 -static gmx_inline gmx_simd_float_t
 -gmx_simd_get_exponent_f_ibm_vmx(gmx_simd_float_t x)
 -{
 -    /* Generate 0x7F800000 without memory operations */
 -    gmx_simd_float_t     expmask = (__vector float)gmx_simd_slli_fi(vec_add(vec_splat_s32(15), vec_sl(vec_splat_s32(15), vec_splat_u32(4))), 23);
 -    gmx_simd_fint32_t    i127    = vec_sub(vec_sl(vec_splat_s32(1), vec_splat_u32(7)), vec_splat_s32(1));
 -    gmx_simd_fint32_t    iexp;
 -
 -    iexp = (__vector int)gmx_simd_and_f(x, expmask);
 -    iexp = vec_sub(gmx_simd_srli_fi(iexp, 23), i127);
 -    return vec_ctf(iexp, 0);
 -}
 -
 -static gmx_inline gmx_simd_float_t
 -gmx_simd_get_mantissa_f_ibm_vmx(gmx_simd_float_t x)
 -{
 -    gmx_simd_float_t     expmask = (__vector float)gmx_simd_slli_fi(vec_add(vec_splat_s32(15), vec_sl(vec_splat_s32(15), vec_splat_u32(4))), 23);
 -
 -    /* Get mantissa. By taking the absolute value (to get rid of the sign bit) we can
 -     * use the same mask as for gmx_simd_get_exponent_f() (but complement it). Since
 -     * these two routines are typically called together, this will save a few operations.
 -     */
 -    x = gmx_simd_andnot_f(expmask, vec_abs(x));
 -    /* Reset zero (but correctly biased) exponent */
 -    return gmx_simd_or_f(x, vec_ctf(vec_splat_s32(1), 0));
 -}
 -
 -static gmx_inline gmx_simd_float_t
 -gmx_simd_set_exponent_f_ibm_vmx(gmx_simd_float_t x)
 -{
 -    gmx_simd_fint32_t  iexp = gmx_simd_cvt_f2i(x);
 -    gmx_simd_fint32_t  i127 = vec_sub(vec_sl(vec_splat_s32(1), vec_splat_u32(7)), vec_splat_s32(1));
 -
 -    iexp = gmx_simd_slli_fi(vec_add(iexp, i127), 23);
 -    return (__vector float)iexp;
 -}
 -
 -static gmx_inline float
 -gmx_simd_reduce_f_ibm_vmx(gmx_simd_float_t x)
 -{
 -    float res;
 -    x = vec_add(x, vec_sld(x, x, 8));
 -    x = vec_add(x, vec_sld(x, x, 4));
 -    vec_ste(x, 0, &res);
 -    return res;
 -}
 -
 -
 -
 -/* SINGLE */
 -#define gmx_simd4_float_t                gmx_simd_float_t
 -#define gmx_simd4_load_f                 gmx_simd_load_f
 -#define gmx_simd4_load1_f                gmx_simd_load1_f
 -#define gmx_simd4_set1_f                 gmx_simd_set1_f
 -#define gmx_simd4_store_f                gmx_simd_store_f
 -#define gmx_simd4_loadu_f                gmx_simd_loadu_f
 -#define gmx_simd4_storeu_f               gmx_simd_storeu_f
 -#define gmx_simd4_setzero_f              gmx_simd_setzero_f
 -#define gmx_simd4_add_f                  gmx_simd_add_f
 -#define gmx_simd4_sub_f                  gmx_simd_sub_f
 -#define gmx_simd4_mul_f                  gmx_simd_mul_f
 -#define gmx_simd4_fmadd_f                gmx_simd_fmadd_f
 -#define gmx_simd4_fmsub_f                gmx_simd_fmsub_f
 -#define gmx_simd4_fnmadd_f               gmx_simd_fnmadd_f
 -#define gmx_simd4_fnmsub_f               gmx_simd_fnmsub_f
 -#define gmx_simd4_and_f                  gmx_simd_and_f
 -#define gmx_simd4_andnot_f               gmx_simd_andnot_f
 -#define gmx_simd4_or_f                   gmx_simd_or_f
 -#define gmx_simd4_xor_f                  gmx_simd_xor_f
 -#define gmx_simd4_rsqrt_f                gmx_simd_rsqrt_f
 -#define gmx_simd4_rcp_f                  gmx_simd_rcp_f
 -#define gmx_simd4_fabs_f                 gmx_simd_fabs_f
 -#define gmx_simd4_fneg_f                 gmx_simd_fneg_f
 -#define gmx_simd4_max_f                  gmx_simd_max_f
 -#define gmx_simd4_min_f                  gmx_simd_min_f
 -#define gmx_simd4_round_f                gmx_simd_round_f
 -#define gmx_simd4_trunc_f                gmx_simd_trunc_f
 -#define gmx_simd4_fraction_f             gmx_simd_fraction_f
 -#define gmx_simd4_get_exponent_f         gmx_simd_get_exponent_f
 -#define gmx_simd4_get_mantissa_f         gmx_simd_get_mantissa_f
 -#define gmx_simd4_set_exponent_f         gmx_simd_set_exponent_f
 -#define gmx_simd4_dotproduct3_f          gmx_simd4_dotproduct3_f_ibm_vmx
 -#define gmx_simd4_fint32_t               gmx_simd_fint32_t
 -#define gmx_simd4_load_fi                gmx_simd_load_fi
 -#define gmx_simd4_load1_fi               gmx_simd_load1_fi
 -#define gmx_simd4_set1_fi                gmx_simd_set1_fi
 -#define gmx_simd4_store_fi               gmx_simd_store_fi
 -#define gmx_simd4_loadu_fi               gmx_simd_loadu_fi
 -#define gmx_simd4_storeu_fi              gmx_simd_storeu_fi
 -#define gmx_simd4_setzero_fi             gmx_simd_setzero_fi
 -#define gmx_simd4_cvt_f2i                gmx_simd_cvt_f2i
 -#define gmx_simd4_cvtt_f2i               gmx_simd_cvtt_f2i
 -#define gmx_simd4_cvt_i2f                gmx_simd_cvt_i2f
 -#define gmx_simd4_fbool_t                gmx_simd_fbool_t
 -#define gmx_simd4_cmpeq_f                gmx_simd_cmpeq_f
 -#define gmx_simd4_cmplt_f                gmx_simd_cmplt_f
 -#define gmx_simd4_cmple_f                gmx_simd_cmple_f
 -#define gmx_simd4_and_fb                 gmx_simd_and_fb
 -#define gmx_simd4_or_fb                  gmx_simd_or_fb
 -#define gmx_simd4_anytrue_fb             gmx_simd_anytrue_fb
 -#define gmx_simd4_blendzero_f            gmx_simd_blendzero_f
 -#define gmx_simd4_blendnotzero_f         gmx_simd_blendnotzero_f
 -#define gmx_simd4_blendv_f               gmx_simd_blendv_f
 -#define gmx_simd4_reduce_f               gmx_simd_reduce_f
 -
 -static gmx_inline float
 -gmx_simd4_dotproduct3_f_ibm_vmx(gmx_simd4_float_t a, gmx_simd4_float_t b)
 -{
 -    gmx_simd4_float_t c = vec_madd(a, b, ((__vector float)vec_splat_u32(0)));
 -    /* Keep only elements 0,1,2 by shifting in zero from right */
 -    c = vec_sld(c, gmx_simd_setzero_f(), 4);
 -    /* calculate sum */
 -    return gmx_simd_reduce_f_ibm_vmx(c);
 -}
 -
 -#endif /* GMX_SIMD_IMPLEMENTATION_IBM_VMX_H */
 +#include "impl_ibm_vmx_definitions.h"
 +#include "impl_ibm_vmx_general.h"
 +// No double precision available for VMX
 +#include "impl_ibm_vmx_simd4_float.h"
 +#include "impl_ibm_vmx_simd_float.h"
 +#include "impl_ibm_vmx_util_float.h"
 +
 +#endif // GMX_SIMD_IMPLEMENTATION_IBM_VMX_H
index 1dcfe1dac8ad88b30c93e16d5b65c87be686e28e,fdc93c9bfc17ba8e221151f4245940eb597b9f06..af7020e9395e129e74add506c511d3ba9a339108
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
-  * Copyright (c) 2014,2015, by the GROMACS development team, led by
+  * Copyright (c) 2014,2015,2016, 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.
@@@ -51,7 -51,7 +51,7 @@@
  #include "buildinfo.h"
  #include "gromacs/utility/directoryenumerator.h"
  #include "gromacs/utility/exceptions.h"
 -#include "gromacs/utility/file.h"
 +#include "gromacs/utility/filestream.h"
  #include "gromacs/utility/path.h"
  #include "gromacs/utility/programcontext.h"
  #include "gromacs/utility/stringutil.h"
@@@ -93,7 -93,7 +93,7 @@@ std::string DataFileFinder::Impl::getDe
   */
  
  DataFileFinder::DataFileFinder()
 -    : impl_(NULL)
 +    : impl_(nullptr)
  {
  }
  
@@@ -109,7 -109,7 +109,7 @@@ void DataFileFinder::setSearchPathFromE
      }
      impl_->envName_ = envVarName;
      const char *const lib = getenv(envVarName);
-     if (lib != NULL)
+     if (!isNullOrEmpty(lib))
      {
          impl_->bEnvIsSet_ = true;
          Path::splitPathEnvironment(lib, &impl_->searchPath_);
@@@ -133,7 -133,7 +133,7 @@@ FILE *DataFileFinder::openFile(const Da
          fprintf(debug, "Opening library file %s\n", fn);
      }
  #endif
 -    return File::openRawHandle(filename, "r");
 +    return TextInputFile::openRawHandle(filename);
  }
  
  std::string DataFileFinder::findFile(const DataFileOptions &options) const
      {
          return options.filename_;
      }
 -    if (impl_.get())
 +    if (impl_ != nullptr)
      {
          std::vector<std::string>::const_iterator i;
          for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
      }
      if (options.bThrow_)
      {
 -        const char *const envName   = (impl_.get() ? impl_->envName_ : NULL);
 -        const bool        bEnvIsSet = (impl_.get() ? impl_->bEnvIsSet_ : false);
 +        const char *const envName   = (impl_ != nullptr ? impl_->envName_ : nullptr);
 +        const bool        bEnvIsSet = (impl_ != nullptr ? impl_->bEnvIsSet_ : false);
          std::string       message(
                  formatString("Library file '%s' not found", options.filename_));
          if (options.bCurrentDir_)
              message.append(Path::getWorkingDirectory());
              message.append(" (current dir)");
          }
 -        if (impl_.get())
 +        if (impl_ != nullptr)
          {
              std::vector<std::string>::const_iterator i;
              for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
@@@ -230,7 -230,7 +230,7 @@@ DataFileFinder::enumerateFiles(const Da
              result.push_back(DataFileInfo(".", *i, false));
          }
      }
 -    if (impl_.get())
 +    if (impl_ != nullptr)
      {
          std::vector<std::string>::const_iterator j;
          for (j = impl_->searchPath_.begin(); j != impl_->searchPath_.end(); ++j)
index 3f3ebf803b3ccc8f18c23f24074e05d8266ceb73,ff2cd0c16243ee48be858880665e15ea6a1f14d0..7d41f71a4fcd5451a6bf6bc1161fec2d314c0aa7
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -
 +/*! \internal \file
 + *
 + * \brief Implements the MD runner routine calling all integrators.
 + *
 + * \author David van der Spoel <david.vanderspoel@icm.uu.se>
 + * \ingroup module_mdlib
 + */
  #include "gmxpre.h"
  
 +#include "runner.h"
 +
  #include "config.h"
  
  #include <assert.h>
  
  #include <algorithm>
  
 +#include "gromacs/commandline/filenm.h"
  #include "gromacs/domdec/domdec.h"
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/essentialdynamics/edsam.h"
  #include "gromacs/ewald/pme.h"
 +#include "gromacs/fileio/checkpoint.h"
 +#include "gromacs/fileio/oenv.h"
  #include "gromacs/fileio/tpxio.h"
 -#include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
 -#include "gromacs/legacyheaders/checkpoint.h"
 -#include "gromacs/legacyheaders/constr.h"
 -#include "gromacs/legacyheaders/copyrite.h"
 -#include "gromacs/legacyheaders/disre.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/gmx_detect_hardware.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/gmx_thread_affinity.h"
 -#include "gromacs/legacyheaders/inputrec.h"
 -#include "gromacs/legacyheaders/main.h"
 -#include "gromacs/legacyheaders/md_logging.h"
 -#include "gromacs/legacyheaders/md_support.h"
 -#include "gromacs/legacyheaders/mdatoms.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/oenv.h"
 -#include "gromacs/legacyheaders/orires.h"
 -#include "gromacs/legacyheaders/qmmm.h"
 -#include "gromacs/legacyheaders/sighandler.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 +#include "gromacs/gmxlib/md_logging.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gpu_utils/gpu_utils.h"
 +#include "gromacs/hardware/detecthardware.h"
 +#include "gromacs/listed-forces/disre.h"
 +#include "gromacs/listed-forces/orires.h"
  #include "gromacs/math/calculate-ewald-splitting-coefficient.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/utilities.h"
  #include "gromacs/math/vec.h"
  #include "gromacs/mdlib/calc_verletbuf.h"
 +#include "gromacs/mdlib/constr.h"
 +#include "gromacs/mdlib/force.h"
 +#include "gromacs/mdlib/forcerec.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/integrator.h"
 +#include "gromacs/mdlib/main.h"
 +#include "gromacs/mdlib/md_support.h"
 +#include "gromacs/mdlib/mdatoms.h"
 +#include "gromacs/mdlib/mdrun.h"
 +#include "gromacs/mdlib/minimize.h"
  #include "gromacs/mdlib/nbnxn_search.h"
 +#include "gromacs/mdlib/qmmm.h"
 +#include "gromacs/mdlib/sighandler.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdlib/tpi.h"
 +#include "gromacs/mdrunutility/threadaffinity.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/state.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/pulling/pull.h"
  #include "gromacs/pulling/pull_rotation.h"
 -#include "gromacs/swap/swapcoords.h"
  #include "gromacs/timing/wallcycle.h"
  #include "gromacs/topology/mtop_util.h"
 +#include "gromacs/trajectory/trajectoryframe.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
  
  #include "deform.h"
 -#include "membed.h"
 +#include "md.h"
  #include "repl_ex.h"
  #include "resource-division.h"
  
  #include "corewrap.h"
  #endif
  
 -typedef struct {
 -    gmx_integrator_t *func;
 -} gmx_intp_t;
 -
 -/* The array should match the eI array in include/types/enums.h */
 -const gmx_intp_t    integrator[eiNR] = { {do_md}, {do_steep}, {do_cg}, {do_md}, {do_md}, {do_nm}, {do_lbfgs}, {do_tpi}, {do_tpi}, {do_md}, {do_md}, {do_md}};
 -
 +//! First step used in pressure scaling
  gmx_int64_t         deform_init_init_step_tpx;
 +//! Initial box for pressure scaling
  matrix              deform_init_box_tpx;
 +//! MPI variable for use in pressure scaling
  tMPI_Thread_mutex_t deform_init_box_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
  
 -
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
  /* The minimum number of atoms per tMPI thread. With fewer atoms than this,
   * the number of threads will get lowered.
   */
  
  struct mdrunner_arglist
  {
 -    gmx_hw_opt_t    hw_opt;
 -    FILE           *fplog;
 -    t_commrec      *cr;
 -    int             nfile;
 -    const t_filenm *fnm;
 -    output_env_t    oenv;
 -    gmx_bool        bVerbose;
 -    gmx_bool        bCompact;
 -    int             nstglobalcomm;
 -    ivec            ddxyz;
 -    int             dd_node_order;
 -    real            rdd;
 -    real            rconstr;
 -    const char     *dddlb_opt;
 -    real            dlb_scale;
 -    const char     *ddcsx;
 -    const char     *ddcsy;
 -    const char     *ddcsz;
 -    const char     *nbpu_opt;
 -    int             nstlist_cmdline;
 -    gmx_int64_t     nsteps_cmdline;
 -    int             nstepout;
 -    int             resetstep;
 -    int             nmultisim;
 -    int             repl_ex_nst;
 -    int             repl_ex_nex;
 -    int             repl_ex_seed;
 -    real            pforce;
 -    real            cpt_period;
 -    real            max_hours;
 -    int             imdport;
 -    unsigned long   Flags;
 +    gmx_hw_opt_t            hw_opt;
 +    FILE                   *fplog;
 +    t_commrec              *cr;
 +    int                     nfile;
 +    const t_filenm         *fnm;
 +    const gmx_output_env_t *oenv;
 +    gmx_bool                bVerbose;
 +    int                     nstglobalcomm;
 +    ivec                    ddxyz;
 +    int                     dd_rank_order;
 +    int                     npme;
 +    real                    rdd;
 +    real                    rconstr;
 +    const char             *dddlb_opt;
 +    real                    dlb_scale;
 +    const char             *ddcsx;
 +    const char             *ddcsy;
 +    const char             *ddcsz;
 +    const char             *nbpu_opt;
 +    int                     nstlist_cmdline;
 +    gmx_int64_t             nsteps_cmdline;
 +    int                     nstepout;
 +    int                     resetstep;
 +    int                     nmultisim;
 +    int                     repl_ex_nst;
 +    int                     repl_ex_nex;
 +    int                     repl_ex_seed;
 +    real                    pforce;
 +    real                    cpt_period;
 +    real                    max_hours;
 +    int                     imdport;
 +    unsigned long           Flags;
  };
  
  
     a commrec. */
  static void mdrunner_start_fn(void *arg)
  {
 -    struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg;
 -    struct mdrunner_arglist  mc  = *mda; /* copy the arg list to make sure
 -                                            that it's thread-local. This doesn't
 -                                            copy pointed-to items, of course,
 -                                            but those are all const. */
 -    t_commrec *cr;                       /* we need a local version of this */
 -    FILE      *fplog = NULL;
 -    t_filenm  *fnm;
 +    try
 +    {
 +        struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg;
 +        struct mdrunner_arglist  mc  = *mda; /* copy the arg list to make sure
 +                                                that it's thread-local. This doesn't
 +                                                copy pointed-to items, of course,
 +                                                but those are all const. */
 +        t_commrec *cr;                       /* we need a local version of this */
 +        FILE      *fplog = NULL;
 +        t_filenm  *fnm;
  
 -    fnm = dup_tfn(mc.nfile, mc.fnm);
 +        fnm = dup_tfn(mc.nfile, mc.fnm);
  
 -    cr = reinitialize_commrec_for_this_thread(mc.cr);
 +        cr = reinitialize_commrec_for_this_thread(mc.cr);
  
 -    if (MASTER(cr))
 -    {
 -        fplog = mc.fplog;
 -    }
 +        if (MASTER(cr))
 +        {
 +            fplog = mc.fplog;
 +        }
  
 -    mdrunner(&mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv,
 -             mc.bVerbose, mc.bCompact, mc.nstglobalcomm,
 -             mc.ddxyz, mc.dd_node_order, mc.rdd,
 -             mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
 -             mc.ddcsx, mc.ddcsy, mc.ddcsz,
 -             mc.nbpu_opt, mc.nstlist_cmdline,
 -             mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
 -             mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 -             mc.cpt_period, mc.max_hours, mc.imdport, mc.Flags);
 +        gmx::mdrunner(&mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv,
 +                      mc.bVerbose, mc.nstglobalcomm,
 +                      mc.ddxyz, mc.dd_rank_order, mc.npme, mc.rdd,
 +                      mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
 +                      mc.ddcsx, mc.ddcsy, mc.ddcsz,
 +                      mc.nbpu_opt, mc.nstlist_cmdline,
 +                      mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
 +                      mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 +                      mc.cpt_period, mc.max_hours, mc.imdport, mc.Flags);
 +    }
 +    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
  }
  
 +
  /* called by mdrunner() to start a specific number of threads (including
     the main thread) for thread-parallel runs. This in turn calls mdrunner()
     for each thread.
     All options besides nthreads are the same as for mdrunner(). */
  static t_commrec *mdrunner_start_threads(gmx_hw_opt_t *hw_opt,
                                           FILE *fplog, t_commrec *cr, int nfile,
 -                                         const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 -                                         gmx_bool bCompact, int nstglobalcomm,
 -                                         ivec ddxyz, int dd_node_order, real rdd, real rconstr,
 +                                         const t_filenm fnm[], const gmx_output_env_t *oenv, gmx_bool bVerbose,
 +                                         int nstglobalcomm,
 +                                         ivec ddxyz, int dd_rank_order, int npme,
 +                                         real rdd, real rconstr,
                                           const char *dddlb_opt, real dlb_scale,
                                           const char *ddcsx, const char *ddcsy, const char *ddcsz,
                                           const char *nbpu_opt, int nstlist_cmdline,
      mda->fnm             = fnmn;
      mda->oenv            = oenv;
      mda->bVerbose        = bVerbose;
 -    mda->bCompact        = bCompact;
      mda->nstglobalcomm   = nstglobalcomm;
      mda->ddxyz[XX]       = ddxyz[XX];
      mda->ddxyz[YY]       = ddxyz[YY];
      mda->ddxyz[ZZ]       = ddxyz[ZZ];
 -    mda->dd_node_order   = dd_node_order;
 +    mda->dd_rank_order   = dd_rank_order;
 +    mda->npme            = npme;
      mda->rdd             = rdd;
      mda->rconstr         = rconstr;
      mda->dddlb_opt       = dddlb_opt;
  #endif /* GMX_THREAD_MPI */
  
  
 -/* We determine the extra cost of the non-bonded kernels compared to
 +/*! \brief Cost of non-bonded kernels
 + *
 + * We determine the extra cost of the non-bonded kernels compared to
   * a reference nstlist value of 10 (which is the default in grompp).
   */
  static const int    nbnxnReferenceNstlist = 10;
 -/* The values to try when switching  */
 +//! The values to try when switching
  const int           nstlist_try[] = { 20, 25, 40 };
 +//! Number of elements in the neighborsearch list trials.
  #define NNSTL  sizeof(nstlist_try)/sizeof(nstlist_try[0])
  /* Increase nstlist until the non-bonded cost increases more than listfac_ok,
   * but never more than listfac_max.
   * nstlist will not be increased enough to reach optimal performance.
   */
  /* CPU: pair-search is about a factor 1.5 slower than the non-bonded kernel */
 +//! Max OK performance ratio beween force calc and neighbor searching
  static const float  nbnxn_cpu_listfac_ok    = 1.05;
 +//! Too high performance ratio beween force calc and neighbor searching
  static const float  nbnxn_cpu_listfac_max   = 1.09;
  /* GPU: pair-search is a factor 1.5-3 slower than the non-bonded kernel */
 +//! Max OK performance ratio beween force calc and neighbor searching
  static const float  nbnxn_gpu_listfac_ok    = 1.20;
 +//! Too high performance ratio beween force calc and neighbor searching
  static const float  nbnxn_gpu_listfac_max   = 1.30;
  
 -/* Try to increase nstlist when using the Verlet cut-off scheme */
 +/*! \brief Try to increase nstlist when using the Verlet cut-off scheme */
  static void increase_nstlist(FILE *fp, t_commrec *cr,
                               t_inputrec *ir, int nstlist_cmdline,
                               const gmx_mtop_t *mtop, matrix box,
      const char            *box_err  = "Can not increase nstlist because the box is too small";
      const char            *dd_err   = "Can not increase nstlist because of domain decomposition limitations";
      char                   buf[STRLEN];
 -    const float            oneThird = 1.0f / 3.0f;
  
      if (nstlist_cmdline <= 0)
      {
      /* Determine the pair list size increase due to zero interactions */
      rlist_inc = nbnxn_get_rlist_effective_inc(ls.cluster_size_j,
                                                mtop->natoms/det(box));
 -    rlist_ok  = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_ok, oneThird) - rlist_inc;
 -    rlist_max = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_max, oneThird) - rlist_inc;
 +    rlist_ok  = (rlistWithReferenceNstlist + rlist_inc)*std::cbrt(listfac_ok) - rlist_inc;
 +    rlist_max = (rlistWithReferenceNstlist + rlist_inc)*std::cbrt(listfac_max) - rlist_inc;
      if (debug)
      {
          fprintf(debug, "nstlist tuning: rlist_inc %.3f rlist_ok %.3f rlist_max %.3f\n",
          calc_verlet_buffer_size(mtop, det(box), ir, -1, &ls, NULL, &rlist_new);
  
          /* Does rlist fit in the box? */
 -        bBox = (sqr(rlist_new) < max_cutoff2(ir->ePBC, box));
 +        bBox = (gmx::square(rlist_new) < max_cutoff2(ir->ePBC, box));
          bDD  = TRUE;
          if (bBox && DOMAINDECOMP(cr))
          {
              fprintf(fp, "%s\n\n", buf);
          }
          ir->rlist     = rlist_new;
 -        ir->rlistlong = rlist_new;
      }
  }
  
 +/*! \brief Initialize variables for Verlet scheme simulation */
  static void prepare_verlet_scheme(FILE                           *fplog,
                                    t_commrec                      *cr,
                                    t_inputrec                     *ir,
                          ls.cluster_size_i, ls.cluster_size_j);
              }
              ir->rlist     = rlist_new;
 -            ir->rlistlong = rlist_new;
          }
      }
  
      }
  }
  
 -/* Override the value in inputrec with value passed on the command line (if any) */
 +/*! \brief Override the nslist value in inputrec
 + *
 + * with value passed on the command line (if any)
 + */
  static void override_nsteps_cmdline(FILE            *fplog,
                                      gmx_int64_t      nsteps_cmdline,
                                      t_inputrec      *ir,
      /* Do nothing if nsteps_cmdline == -2 */
  }
  
 +namespace gmx
 +{
 +
 +//! \brief Return the correct integrator function.
 +static integrator_t *my_integrator(unsigned int ei)
 +{
 +    switch (ei)
 +    {
 +        case eiMD:
 +        case eiBD:
 +        case eiSD1:
 +        case eiVV:
 +        case eiVVAK:
 +            if (!EI_DYNAMICS(ei))
 +            {
 +                GMX_THROW(APIError("do_md integrator would be called for a non-dynamical integrator"));
 +            }
 +            return do_md;
 +        case eiSteep:
 +            return do_steep;
 +        case eiCG:
 +            return do_cg;
 +        case eiNM:
 +            return do_nm;
 +        case eiLBFGS:
 +            return do_lbfgs;
 +        case eiTPI:
 +        case eiTPIC:
 +            if (!EI_TPI(ei))
 +            {
 +                GMX_THROW(APIError("do_tpi integrator would be called for a non-TPI integrator"));
 +            }
 +            return do_tpi;
 +        case eiSD2_REMOVED:
 +            GMX_THROW(NotImplementedError("SD2 integrator has been removed"));
 +        default:
 +            GMX_THROW(APIError("Non existing integrator selected"));
 +    }
 +}
 +
  int mdrunner(gmx_hw_opt_t *hw_opt,
               FILE *fplog, t_commrec *cr, int nfile,
 -             const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 -             gmx_bool bCompact, int nstglobalcomm,
 -             ivec ddxyz, int dd_node_order, real rdd, real rconstr,
 +             const t_filenm fnm[], const gmx_output_env_t *oenv, gmx_bool bVerbose,
 +             int nstglobalcomm,
 +             ivec ddxyz, int dd_rank_order, int npme, real rdd, real rconstr,
               const char *dddlb_opt, real dlb_scale,
               const char *ddcsx, const char *ddcsy, const char *ddcsz,
               const char *nbpu_opt, int nstlist_cmdline,
      gmx_constr_t              constr;
      int                       nChargePerturbed = -1, nTypePerturbed = 0, status;
      gmx_wallcycle_t           wcycle;
 -    gmx_bool                  bReadEkin;
      gmx_walltime_accounting_t walltime_accounting = NULL;
      int                       rc;
      gmx_int64_t               reset_counters;
      gmx_edsam_t               ed           = NULL;
      int                       nthreads_pme = 1;
 -    int                       nthreads_pp  = 1;
 -    gmx_membed_t              membed       = NULL;
      gmx_hw_info_t            *hwinfo       = NULL;
      /* The master rank decides early on bUseGPU and broadcasts this later */
 -    gmx_bool                  bUseGPU      = FALSE;
 +    gmx_bool                  bUseGPU            = FALSE;
  
      /* CAUTION: threads may be started later on in this function, so
         cr doesn't reflect the final parallel state right now */
      if (SIMMASTER(cr))
      {
          /* Read (nearly) all data required for the simulation */
 -        read_tpx_state(ftp2fn(efTPR, nfile, fnm), inputrec, state, NULL, mtop);
 +        read_tpx_state(ftp2fn(efTPR, nfile, fnm), inputrec, state, mtop);
  
          if (inputrec->cutoff_scheme == ecutsVERLET)
          {
                  gmx_fatal(FARGS, "GPU requested, but can't be used without cutoff-scheme=Verlet");
              }
  
 -#ifdef GMX_TARGET_BGQ
 +#if GMX_TARGET_BGQ
              md_print_warn(cr, fplog,
                            "NOTE: There is no SIMD implementation of the group scheme kernels on\n"
                            "      BlueGene/Q. You will observe better performance from using the\n"
                            "      Verlet cut-off scheme.\n");
  #endif
          }
 -
 -        if (inputrec->eI == eiSD2)
 -        {
 -            md_print_warn(cr, fplog, "The stochastic dynamics integrator %s is deprecated, since\n"
 -                          "it is slower than integrator %s and is slightly less accurate\n"
 -                          "with constraints. Use the %s integrator.",
 -                          ei_names[inputrec->eI], ei_names[eiSD1], ei_names[eiSD1]);
 -        }
      }
  
      /* Check and update the hardware options for internal consistency */
 -    check_and_update_hw_opt_1(hw_opt, cr);
 +    check_and_update_hw_opt_1(hw_opt, cr, npme);
  
      /* Early check for externally set process affinity. */
      gmx_check_thread_affinity_set(fplog, cr,
                                    hw_opt, hwinfo->nthreads_hw_avail, FALSE);
  
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
      if (SIMMASTER(cr))
      {
 -        if (cr->npmenodes > 0 && hw_opt->nthreads_tmpi <= 0)
 +        if (npme > 0 && hw_opt->nthreads_tmpi <= 0)
          {
              gmx_fatal(FARGS, "You need to explicitly specify the number of MPI threads (-ntmpi) when using separate PME ranks");
          }
              t_commrec *cr_old       = cr;
              /* now start the threads. */
              cr = mdrunner_start_threads(hw_opt, fplog, cr_old, nfile, fnm,
 -                                        oenv, bVerbose, bCompact, nstglobalcomm,
 -                                        ddxyz, dd_node_order, rdd, rconstr,
 +                                        oenv, bVerbose, nstglobalcomm,
 +                                        ddxyz, dd_rank_order, npme, rdd, rconstr,
                                          dddlb_opt, dlb_scale, ddcsx, ddcsy, ddcsz,
                                          nbpu_opt, nstlist_cmdline,
                                          nsteps_cmdline, nstepout, resetstep, nmultisim,
  #endif
      /* END OF CAUTION: cr is now reliable */
  
 -    /* g_membed initialisation *
 -     * Because we change the mtop, init_membed is called before the init_parallel *
 -     * (in case we ever want to make it run in parallel) */
 -    if (opt2bSet("-membed", nfile, fnm))
 -    {
 -        if (MASTER(cr))
 -        {
 -            fprintf(stderr, "Initializing membed");
 -        }
 -        membed = init_membed(fplog, nfile, fnm, mtop, inputrec, state, cr, &cpt_period);
 -    }
 -
      if (PAR(cr))
      {
          /* now broadcast everything to the non-master nodes/threads: */
      /* A parallel command line option consistency check that we can
         only do after any threads have started. */
      if (!PAR(cr) &&
 -        (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || cr->npmenodes > 0))
 +        (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || npme > 0))
      {
          gmx_fatal(FARGS,
                    "The -dd or -npme option request a parallel simulation, "
 -#ifndef GMX_MPI
 +#if !GMX_MPI
                    "but %s was compiled without threads or MPI enabled"
  #else
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
-                   "but the number of threads (option -nt) is 1"
+                   "but the number of MPI-threads (option -ntmpi) is not set or is 1"
  #else
                    "but %s was not started through mpirun/mpiexec or only one rank was requested through mpirun/mpiexec"
  #endif
  
      if (!(EEL_PME(inputrec->coulombtype) || EVDW_PME(inputrec->vdwtype)))
      {
 -        if (cr->npmenodes > 0)
 +        if (npme > 0)
          {
 -            gmx_fatal_collective(FARGS, cr, NULL,
 +            gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
                                   "PME-only ranks are requested, but the system does not use PME for electrostatics or LJ");
          }
  
 -        cr->npmenodes = 0;
 +        npme = 0;
      }
  
 -    if (bUseGPU && cr->npmenodes < 0)
 +    if (bUseGPU && npme < 0)
      {
          /* With GPUs we don't automatically use PME-only ranks. PME ranks can
           * improve performance with many threads per GPU, since our OpenMP
           * scaling is bad, but it's difficult to automate the setup.
           */
 -        cr->npmenodes = 0;
 +        npme = 0;
      }
  
  #ifdef GMX_FAHCORE
      init_orires(fplog, mtop, state->x, inputrec, cr, &(fcd->orires),
                  state);
  
 -    if (DEFORM(*inputrec))
 +    if (inputrecDeform(inputrec))
      {
          /* Store the deform reference box before reading the checkpoint */
          if (SIMMASTER(cr))
          tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
      }
  
 -    if (opt2bSet("-cpi", nfile, fnm))
 +    if (Flags & MD_STARTFROMCPT)
      {
          /* Check if checkpoint file exists before doing continuation.
           * This way we can use identical input options for the first and subsequent runs...
           */
 -        if (gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr) )
 -        {
 -            load_checkpoint(opt2fn_master("-cpi", nfile, fnm, cr), &fplog,
 -                            cr, ddxyz,
 -                            inputrec, state, &bReadEkin,
 -                            (Flags & MD_APPENDFILES),
 -                            (Flags & MD_APPENDFILESSET));
 +        gmx_bool bReadEkin;
  
 -            if (bReadEkin)
 -            {
 -                Flags |= MD_READ_EKIN;
 -            }
 +        load_checkpoint(opt2fn_master("-cpi", nfile, fnm, cr), &fplog,
 +                        cr, ddxyz, &npme,
 +                        inputrec, state, &bReadEkin,
 +                        (Flags & MD_APPENDFILES),
 +                        (Flags & MD_APPENDFILESSET));
 +
 +        if (bReadEkin)
 +        {
 +            Flags |= MD_READ_EKIN;
          }
      }
  
          gmx_bcast(sizeof(box), box, cr);
      }
  
 +    // TODO This should move to do_md(), because it only makes sense
 +    // with dynamical integrators, but there is no test coverage and
 +    // it interacts with constraints, somehow.
      /* Essential dynamics */
      if (opt2bSet("-ei", nfile, fnm))
      {
      if (PAR(cr) && !(EI_TPI(inputrec->eI) ||
                       inputrec->eI == eiNM))
      {
 -        cr->dd = init_domain_decomposition(fplog, cr, Flags, ddxyz, rdd, rconstr,
 +        cr->dd = init_domain_decomposition(fplog, cr, Flags, ddxyz, npme,
 +                                           dd_rank_order,
 +                                           rdd, rconstr,
                                             dddlb_opt, dlb_scale,
                                             ddcsx, ddcsy, ddcsz,
                                             mtop, inputrec,
                                             box, state->x,
                                             &ddbox, &npme_major, &npme_minor);
 -
 -        make_dd_communicators(fplog, cr, dd_node_order);
 -
 -        /* Set overallocation to avoid frequent reallocation of arrays */
 -        set_over_alloc_dd(TRUE);
      }
      else
      {
  
      /* Initialize per-physical-node MPI process/thread ID and counters. */
      gmx_init_intranode_counters(cr);
 -#ifdef GMX_MPI
 +#if GMX_MPI
      if (MULTISIM(cr))
      {
          md_print_info(cr, fplog,
      }
      md_print_info(cr, fplog, "Using %d MPI %s\n",
                    cr->nnodes,
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
                    cr->nnodes == 1 ? "thread" : "threads"
  #else
                    cr->nnodes == 1 ? "process" : "processes"
                            inputrec->cutoff_scheme == ecutsVERLET);
  
  #ifndef NDEBUG
 -    if (integrator[inputrec->eI].func != do_tpi &&
 +    if (EI_TPI(inputrec->eI) &&
          inputrec->cutoff_scheme == ecutsVERLET)
      {
          gmx_feenableexcept();
         PME: env variable should be read only on one node to make sure it is
         identical everywhere;
       */
 -    /* TODO nthreads_pp is only used for pinning threads.
 -     * This is a temporary solution until we have a hw topology library.
 -     */
 -    nthreads_pp  = gmx_omp_nthreads_get(emntNonbonded);
      nthreads_pme = gmx_omp_nthreads_get(emntPME);
  
 -    wcycle = wallcycle_init(fplog, resetstep, cr, nthreads_pp, nthreads_pme);
 +    wcycle = wallcycle_init(fplog, resetstep, cr);
  
      if (PAR(cr))
      {
          fr          = mk_forcerec();
          fr->hwinfo  = hwinfo;
          fr->gpu_opt = &hw_opt->gpu_opt;
 -        init_forcerec(fplog, oenv, fr, fcd, inputrec, mtop, cr, box,
 +        init_forcerec(fplog, fr, fcd, inputrec, mtop, cr, box,
                        opt2fn("-table", nfile, fnm),
 -                      opt2fn("-tabletf", nfile, fnm),
                        opt2fn("-tablep", nfile, fnm),
                        opt2fn("-tableb", nfile, fnm),
                        nbpu_opt,
                        FALSE,
                        pforce);
  
 -        /* version for PCA_NOT_READ_NODE (see md.c) */
 -        /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
 -           "nofile","nofile","nofile","nofile",FALSE,pforce);
 -         */
 -
          /* Initialize QM-MM */
          if (fr->bQMMM)
          {
      }
  
  
 -    if (integrator[inputrec->eI].func == do_md)
 +    if (EI_DYNAMICS(inputrec->eI))
      {
          /* Turn on signal handling on all nodes */
          /*
          if (inputrec->bRot)
          {
              /* Initialize enforced rotation code */
 -            init_rot(fplog, inputrec, nfile, fnm, cr, state->x, box, mtop, oenv,
 +            init_rot(fplog, inputrec, nfile, fnm, cr, state->x, state->box, mtop, oenv,
                       bVerbose, Flags);
          }
  
 -        if (inputrec->eSwapCoords != eswapNO)
 -        {
 -            /* Initialize ion swapping code */
 -            init_swapcoords(fplog, bVerbose, inputrec, opt2fn_master("-swap", nfile, fnm, cr),
 -                            mtop, state->x, state->box, &state->swapstate, cr, oenv, Flags);
 -        }
 -
          constr = init_constraints(fplog, mtop, inputrec, ed, state, cr);
  
          if (DOMAINDECOMP(cr))
          {
              GMX_RELEASE_ASSERT(fr, "fr was NULL while cr->duty was DUTY_PP");
 +            /* This call is not included in init_domain_decomposition mainly
 +             * because fr->cginfo_mb is set later.
 +             */
              dd_init_bondeds(fplog, cr->dd, mtop, vsite, inputrec,
                              Flags & MD_DDBONDCHECK, fr->cginfo_mb);
 -
 -            set_dd_parameters(fplog, cr->dd, dlb_scale, inputrec, &ddbox);
 -
 -            setup_dd_grid(fplog, cr->dd);
          }
  
          /* Now do whatever the user wants us to do (how flexible...) */
 -        integrator[inputrec->eI].func(fplog, cr, nfile, fnm,
 -                                      oenv, bVerbose, bCompact,
 -                                      nstglobalcomm,
 -                                      vsite, constr,
 -                                      nstepout, inputrec, mtop,
 -                                      fcd, state,
 -                                      mdatoms, nrnb, wcycle, ed, fr,
 -                                      repl_ex_nst, repl_ex_nex, repl_ex_seed,
 -                                      membed,
 -                                      cpt_period, max_hours,
 -                                      imdport,
 -                                      Flags,
 -                                      walltime_accounting);
 +        my_integrator(inputrec->eI) (fplog, cr, nfile, fnm,
 +                                     oenv, bVerbose,
 +                                     nstglobalcomm,
 +                                     vsite, constr,
 +                                     nstepout, inputrec, mtop,
 +                                     fcd, state,
 +                                     mdatoms, nrnb, wcycle, ed, fr,
 +                                     repl_ex_nst, repl_ex_nex, repl_ex_seed,
 +                                     cpt_period, max_hours,
 +                                     imdport,
 +                                     Flags,
 +                                     walltime_accounting);
  
 -        if (inputrec->bPull)
 +        if (inputrec->bRot)
          {
 -            finish_pull(inputrec->pull_work);
 +            finish_rot(inputrec->rot);
          }
  
 -        if (inputrec->bRot)
 +        if (inputrec->bPull)
          {
 -            finish_rot(inputrec->rot);
 +            finish_pull(inputrec->pull_work);
          }
  
      }
      /* Free GPU memory and context */
      free_gpu_resources(fr, cr, &hwinfo->gpu_info, fr ? fr->gpu_opt : NULL);
  
 -    if (opt2bSet("-membed", nfile, fnm))
 -    {
 -        sfree(membed);
 -    }
 -
      gmx_hardware_info_free(hwinfo);
  
      /* Does what it says */
  
      done_ed(&ed);
  
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
      /* we need to join all threads. The sub-threads join when they
         exit this function, but the master thread needs to be told to
         wait for that. */
  
      return rc;
  }
 +
 +} // namespace gmx