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

Change-Id: I45a70eee6f7ec4dd922b6c04c740c40f50159a45

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

diff --combined CMakeLists.txt
index 03f24b6f0ca6e056031a86d5cb99dc4e9dff8f43,e69ee1ef7252419f847b6de7b8eb5724caaca814..4f1f62b6dd27e5f3becd3a12f3f3bbf730691fc0
@@@ -1,10 -1,10 +1,10 @@@
  #
  # This file is part of the GROMACS molecular simulation package.
  #
 -# Copyright (c) 2012,2013, by the GROMACS development team, led by
 -# David van der Spoel, Berk Hess, Erik Lindahl, and including many
 -# others, as listed in the AUTHORS file in the top-level source
 -# directory and at http://www.gromacs.org.
 +# Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
 +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 +# and including many others, as listed in the AUTHORS file in the
 +# top-level source directory and at http://www.gromacs.org.
  #
  # GROMACS is free software; you can redistribute it and/or
  # modify it under the terms of the GNU Lesser General Public License
  #
  # To help us fund GROMACS development, we humbly ask that you cite
  # the research papers on the package. Check out http://www.gromacs.org.
 -#
 -cmake_minimum_required(VERSION 2.8)
 -# Keep CMake suitably quiet on Cygwin
 -set(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required
  
 -# Allows CPack to act differently for normal tools and mdrun (e.g. because of MPI)
 -set(CPACK_COMPONENT_GROUP_TOOLS_DESCRIPTION "All GROMACS executable tools")
 -set(CPACK_COMPONENT_GROUP_MDRUN_DESCRIPTION "GROMACS executable for running simulations")
 +cmake_minimum_required(VERSION 2.8.8)
  
  # CMake modules/macros are in a subdirectory to keep this file cleaner
  # This needs to be set before project() in order to pick up toolchain files
  list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform)
  
 -project(Gromacs C)
 -include(Dart)
 -mark_as_advanced(DART_ROOT)
 +project(Gromacs)
 +
 +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
  
 -# PROJECT_VERSION should have the following structure: 
 +# PROJECT_VERSION should have the following structure:
  # VERSION-dev[-SUFFIX] where the VERSION should have the for: vMajor.vMinor.vPatch
  #
 -# The "-dev" suffix is important to keep because it makes possible to distinguish 
 -# between a build from official release and a build from git release branch on a 
 -# machine with no git. 
 +# The "-dev" suffix is important to keep because it makes possible to distinguish
 +# between a build from official release and a build from git release branch on a
 +# machine with no git.
  #
  # NOTE: when releasing the "-dev" suffix needs to be stripped off!
 -set(PROJECT_VERSION "4.6.6-dev")
 -# The version number of the regressiontest tarball against which this
 -# git branch can be tested. Normally, this will be the version of the
 -# last patch release. Comment the next line out for branches leading
 -# to a major/minor release.
 -set(REGRESSIONTEST_VERSION "4.6.5")
 +# REGRESSIONTEST_VERSION and REGRESSIONTEST_BRANCH should always be
 +# defined.
 +set(PROJECT_VERSION "5.0-rc1-dev")
 +# If this is a released tarball, "-dev" will not be present in
 +# PROJECT_VERSION, and REGRESSIONTEST_VERSION specifies the version
 +# number of the regressiontest tarball against which the code tarball
 +# can be tested. This will be the version of the last patch release.
 +set(REGRESSIONTEST_VERSION "5.0-rc1")
 +# If this is not a released tarball, "-dev" will be present in
 +# PROJECT_VERSION, and REGRESSIONTEST_BRANCH specifies the name of the
 +# gerrit.gromacs.org branch whose HEAD can test this code, *if* this
 +# code contains all recent fixes from the corresponding code branch.
 +set(REGRESSIONTEST_BRANCH "refs/heads/release-5-0")
 +
  set(CUSTOM_VERSION_STRING ""
      CACHE STRING "Custom version string (if empty, use hard-coded default)")
  mark_as_advanced(CUSTOM_VERSION_STRING)
  if (CUSTOM_VERSION_STRING)
      set(PROJECT_VERSION ${CUSTOM_VERSION_STRING})
 -endif (CUSTOM_VERSION_STRING)
 -set(SOVERSION 8)
 +endif()
 +set(LIBRARY_SOVERSION 0)
 +set(LIBRARY_VERSION ${LIBRARY_SOVERSION}.0.0)
  # It is a bit irritating, but this has to be set separately for now!
 -SET(CPACK_PACKAGE_VERSION_MAJOR "4")
 -SET(CPACK_PACKAGE_VERSION_MINOR "6")
 -SET(CPACK_PACKAGE_VERSION_PATCH "1")
 +SET(CPACK_PACKAGE_VERSION_MAJOR "5")
 +SET(CPACK_PACKAGE_VERSION_MINOR "0")
 +#SET(CPACK_PACKAGE_VERSION_PATCH "0")
  
  # The numerical gromacs version. It is 40600 for 4.6.0.
 -# The #define GMX_VERSION in gmx_header_config_h is set to this value.
 -math(EXPR NUM_VERSION 
 +# The #define GMX_VERSION in gromacs/version.h is set to this value.
 +math(EXPR NUM_VERSION
      "${CPACK_PACKAGE_VERSION_MAJOR}*10000 + ${CPACK_PACKAGE_VERSION_MINOR}*100")
  if(CPACK_PACKAGE_VERSION_PATCH)
 -    math(EXPR NUM_VERSION 
 +    math(EXPR NUM_VERSION
           "${NUM_VERSION} + ${CPACK_PACKAGE_VERSION_PATCH}")
  endif()
  
  set(API_VERSION ${NUM_VERSION})
  
  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND UNIX)
 -set(CMAKE_INSTALL_PREFIX "/usr/local/gromacs" CACHE STRING "Installation prefix (installation will need write permissions here)" FORCE)
 +    set(CMAKE_INSTALL_PREFIX "/usr/local/gromacs" CACHE STRING "Installation prefix (installation will need write permissions here)" FORCE)
  endif()
  
 +set(GMX_INSTALL_PREFIX "" CACHE STRING "Prefix gets appended to CMAKE_INSTALL_PREFIX. For cpack it sets the root folder of the archive.")
 +mark_as_advanced(GMX_INSTALL_PREFIX)
 +
  include(gmxBuildTypeReference)
 +include(gmxBuildTypeThreadSanitizer)
 +include(gmxBuildTypeReleaseWithAssert)
  
  if(NOT CMAKE_BUILD_TYPE)
 -    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference." FORCE)
 -endif(NOT CMAKE_BUILD_TYPE)
 +    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert." FORCE)
 +    # There's no need to offer a user the choice of ThreadSanitizer
 +    # Set the possible values of build type for cmake-gui
 +    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
 +        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert")
 +endif()
 +if(CMAKE_CONFIGURATION_TYPES)
 +    # Add appropriate GROMACS-specific build types for the Visual
 +    # Studio generator (Debug, Release, MinSizeRel and RelWithDebInfo
 +    # are already present by default).
 +    list(APPEND CMAKE_CONFIGURATION_TYPES "RelWithAssert" "Reference")
 +    list(REMOVE_DUPLICATES CMAKE_CONFIGURATION_TYPES)
 +    set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING
 +        "List of configuration types"
 +        FORCE)
 +endif()
 +set(build_types_with_explicit_flags RELEASE DEBUG RELWITHDEBUGINFO RELWITHASSERT MINSIZEREL)
  
  enable_language(C)
 -
 -set(GMX_USE_RELATIVE_INSTALL_PATH OFF CACHE STRING "Use relative paths not absolute paths for cmake install. Has only an effect on cpack.")
 -mark_as_advanced(GMX_USE_RELATIVE_INSTALL_PATH)
 +enable_language(CXX)
  
  set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
  set(CPACK_PACKAGE_VENDOR "gromacs.org")
  set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Gromacs - a toolkit for high-performance molecular simulation")
 -if (NOT GMX_USE_RELATIVE_INSTALL_PATH)
 -    set(CPACK_SET_DESTDIR "ON")
 -endif()
  set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_SOURCE_DIR}/admin/InstallWelcome.txt")
  # Its GPL/LGPL, so they do not have to agree to a license for mere usage, but some installers require this...
  set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING")
  set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/admin/InstallInfo.txt")
  set(CPACK_SOURCE_IGNORE_FILES "\\\\.isreposource$;\\\\.git/;\\\\.gitignore$")
  set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_SOURCE_DIR}/CPackInit.cmake")
 -SET(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR};/;${CMAKE_BINARY_DIR}/man;man")
 +set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR};/;${CMAKE_BINARY_DIR}/src/programs/completion;src/programs/completion;${CMAKE_BINARY_DIR}/share/man/man1;share/man/man1;${CMAKE_BINARY_DIR}/share/man/man7;share/man/man7;${CMAKE_BINARY_DIR}/share/html/final;share/html/final")
  set(CPACK_PACKAGE_CONTACT "gmx-users@gromacs.org")
  
  #must come after all cpack settings!
  include(CPack)
  
 +# Set a default valgrind suppression file.
 +# This unfortunately needs to duplicate information from CTest to work as
 +# expected...
 +set(MEMORYCHECK_SUPPRESSIONS_FILE
 +    "${CMAKE_SOURCE_DIR}/cmake/legacy_and_external.supp"
 +    CACHE FILEPATH
 +    "File that contains suppressions for the memory checker")
 +include(CTest)
 +
 +set(SOURCE_IS_GIT_REPOSITORY OFF)
 +set(SOURCE_IS_SOURCE_DISTRIBUTION OFF)
 +if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
 +    set(SOURCE_IS_GIT_REPOSITORY ON)
 +endif()
 +if(NOT EXISTS "${CMAKE_SOURCE_DIR}/admin/.isreposource")
 +    set(SOURCE_IS_SOURCE_DISTRIBUTION ON)
 +endif()
 +
  ########################################################################
  # Check and warn if cache generated on a different host is being reused
  ########################################################################
@@@ -179,104 -141,77 +179,104 @@@ if(CMAKE_HOST_UNIX
  endif()
  
  ########################################################################
 -# User input options - enable C++ - before any CXX flags are changed   #
 +# User input options                                                   #
  ########################################################################
 +include(gmxOptionUtilities)
  
 -# decide on GPU settings based on user-settings and GPU/CUDA detection
 -include(gmxManageGPU)
 -
 -option(GMX_FORCE_CXX "Enable C++ compilation even if not necessary" OFF)
 -mark_as_advanced(GMX_FORCE_CXX)
 -
 -option(GMX_COOL_QUOTES "Enable Gromacs cool quotes" ON)
 -mark_as_advanced(GMX_COOL_QUOTES)
 -
 -if(GMX_GPU OR GMX_FORCE_CXX OR GMX_OPENMM)
 -    enable_language(CXX)
 -endif()
  set(CMAKE_PREFIX_PATH "" CACHE STRING "Extra locations to search for external libraries and tools (give directory without lib, bin, or include)")
  
 -########################################################################
 -# User input options                                                   #
 -########################################################################
  option(GMX_DOUBLE "Use double precision (much slower, use only if you really need it)" OFF)
  option(GMX_MPI    "Build a parallel (message-passing) version of GROMACS" OFF)
  option(GMX_THREAD_MPI  "Build a thread-MPI-based multithreaded version of GROMACS (not compatible with MPI)" ON)
 +gmx_dependent_option(
 +    GMX_MPI_IN_PLACE
 +    "Enable MPI_IN_PLACE for MPIs that have it defined"
 +    ON
 +    GMX_MPI)
 +mark_as_advanced(GMX_MPI_IN_PLACE)
  option(GMX_SOFTWARE_INVSQRT "Use GROMACS software 1/sqrt" ON)
  mark_as_advanced(GMX_SOFTWARE_INVSQRT)
  option(GMX_FAHCORE "Build a library with mdrun functionality" OFF)
  mark_as_advanced(GMX_FAHCORE)
  
 -if(NOT DEFINED GMX_CPU_ACCELERATION AND NOT CMAKE_CROSSCOMPILING)
 -    include(gmxDetectAcceleration)
 -    gmx_detect_acceleration(GMX_SUGGESTED_CPU_ACCELERATION)
 -endif()
 +option(GMX_COOL_QUOTES "Enable Gromacs cool quotes" ON)
 +mark_as_advanced(GMX_COOL_QUOTES)
 +gmx_add_cache_dependency(GMX_COOL_QUOTES BOOL "NOT GMX_FAHCORE" OFF)
 +
 +# decide on GPU settings based on user-settings and GPU/CUDA detection
 +include(gmxManageGPU)
  
  # Detect the architecture the compiler is targetting, detect
 -# acceleration possibilities on that hardware, suggest an acceleration
 +# SIMD instructions possibilities on that hardware, suggest SIMD instruction set
  # to use if none is specified, and populate the cache option for CPU
 -# accleration.
 +# SIMD.
  include(gmxDetectTargetArchitecture)
  gmx_detect_target_architecture()
 -include(gmxDetectAcceleration)
 -gmx_detect_acceleration(GMX_SUGGESTED_CPU_ACCELERATION)
 -set(GMX_CPU_ACCELERATION "@GMX_SUGGESTED_CPU_ACCELERATION@"
 -    CACHE STRING "Accelerated CPU kernels. Pick one of: None, SSE2, SSE4.1, AVX_128_FMA, AVX_256, IBM_QPX, Sparc64_HPC_ACE")
 -
 -set(GMX_FFT_LIBRARY "fftw3" 
 -    CACHE STRING "FFT library choices: fftw3,mkl,fftpack[built-in]")
 -option(GMX_BUILD_OWN_FFTW "Download and build FFTW 3 during the GROMACS build process, rather than fall back on the really slow fftpack." OFF)
 +
 +if(GMX_CPU_ACCELERATION)
 +    # Stay compatible with old Jenkins command line options for specific SIMD acceleration
 +    set(GMX_SIMD "${GMX_CPU_ACCELERATION}" CACHE STRING "SIMD instruction set level and compiler optimization" FORCE)
 +endif()
 +
 +if(NOT GMX_TARGET_MIC)
 +    include(gmxDetectSimd)
 +    gmx_detect_simd(GMX_SUGGESTED_SIMD)
 +else()
 +    set(GMX_SUGGESTED_SIMD "None")
 +endif()
 +
 +gmx_option_multichoice(
 +    GMX_SIMD
 +    "SIMD instruction set for CPU kernels and compiler optimization"
 +    "${GMX_SUGGESTED_SIMD}"
 +    None SSE2 SSE4.1 AVX_128_FMA AVX_256 AVX2_256 IBM_QPX Sparc64_HPC_ACE Reference)
 +
 +gmx_option_multichoice(
 +    GMX_FFT_LIBRARY
 +    "FFT library"
 +    "fftw3"
 +    fftw3 mkl "fftpack[built-in]")
 +gmx_dependent_option(
 +    GMX_BUILD_OWN_FFTW
 +    "Download and build FFTW 3 during the GROMACS build process, rather than fall back on the really slow fftpack."
 +    OFF
 +    "GMX_FFT_LIBRARY STREQUAL FFTW3")
 +gmx_dependent_option(
 +    GMX_DISABLE_FFTW_MEASURE
 +    "Do not optimize FFTW setups (not needed with SSE)"
 +    OFF
 +    "GMX_FFT_LIBRARY STREQUAL FFTW3")
  mark_as_advanced(GMX_BUILD_OWN_FFTW)
 -option(GMX_DISABLE_FFTW_MEASURE 
 -       "Do not optimize FFTW setups (not needed with SSE)" OFF)
  mark_as_advanced(GMX_DISABLE_FFTW_MEASURE)
 -set(GMX_QMMM_PROGRAM "none" 
 -    CACHE STRING "QM package choices: none,gaussian,mopac,gamess,orca")
 +
 +gmx_option_multichoice(
 +    GMX_QMMM_PROGRAM
 +    "QM package for QM/MM"
 +    None
 +    none gaussian mopac gamess orca)
 +
 +gmx_dependent_cache_variable(GMX_SIMD_REF_FLOAT_WIDTH  "Reference SIMD single precision width" STRING "4" "GMX_SIMD STREQUAL REFERENCE")
 +gmx_dependent_cache_variable(GMX_SIMD_REF_DOUBLE_WIDTH "Reference SIMD double precision width" STRING "2" "GMX_SIMD STREQUAL REFERENCE")
 +
 +# This should be moved to a separate NBNXN cmake module when that code is cleaned up and modularized
 +
  option(GMX_BROKEN_CALLOC "Work around broken calloc()" OFF)
  mark_as_advanced(GMX_BROKEN_CALLOC)
 -option(GMX_MPI_IN_PLACE "Enable MPI_IN_PLACE for MPIs that have it defined" ON)
 -mark_as_advanced(GMX_MPI_IN_PLACE)
  option(GMX_LOAD_PLUGINS "Compile with plugin support, needed to read VMD supported file formats" ON)
  mark_as_advanced(GMX_LOAD_PLUGINS)
  
 +option(GMX_GPU  "Enable GPU acceleration" ON)
  option(GMX_OPENMP "Enable OpenMP-based multithreading" ON)
  
 -option(USE_VERSION_H "Generate development version string/information" ON)
 -mark_as_advanced(USE_VERSION_H)
 +option(GMX_USE_TNG "Use the TNG library for trajectory I/O" ON)
  
 -option(GMX_DEFAULT_SUFFIX "Use default suffixes for GROMACS binaries and libs (_d for double, _mpi for MPI; rerun cmake after changing to see relevant options)" ON)
 +option(GMX_GIT_VERSION_INFO "Generate git version information" ${SOURCE_IS_GIT_REPOSITORY})
 +mark_as_advanced(GMX_GIT_VERSION_INFO)
  
  if(UNIX)
 -    option(GMX_PREFER_STATIC_LIBS "When finding libraries prefer static archives (not available on non-*nix platforms and it will only work if static versions of external dependencies are available and found)!" OFF)
 -    mark_as_advanced(GMX_PREFER_STATIC_LIBS)
 +    option(GMX_SYMLINK_OLD_BINARY_NAMES "Create symbolic links for pre-5.0 binary names" ON)
  endif()
 +option(GMX_BUILD_MDRUN_ONLY "Build and install only the mdrun binary" OFF)
  
  option(GMX_CYCLE_SUBCOUNTERS "Enable cycle subcounters to get a more detailed cycle timings" OFF)
  mark_as_advanced(GMX_CYCLE_SUBCOUNTERS)
  option(GMX_SKIP_DEFAULT_CFLAGS "Don't automatically add suggested/required Compiler flags." OFF)
  mark_as_advanced(GMX_SKIP_DEFAULT_CFLAGS)
  
 +option(GMX_BUILD_FOR_COVERAGE
 +       "Tune build for better code coverage metrics (e.g., disable asserts)"
 +       OFF)
 +mark_as_advanced(GMX_BUILD_FOR_COVERAGE)
 +
 +
 +######################################################################
 +# Detect OpenMP support
 +######################################################################
 +# The OpenMP detection _must_ come before tests for other CFLAGS.
 +include(gmxManageOpenMP)
 +
 +
 +
  ######################################################################
  # Compiler tests
  # These need to be done early (before further tests).
  # These files should be removed from the source tree when a CMake version that
  # includes the features in question becomes required for building GROMACS.
  include(CheckCCompilerFlag)
 -if(CMAKE_CXX_COMPILER_LOADED)
 -    include(CheckCXXCompilerFlag)
 -endif()
 -
 -# Get compiler version information, needs to be done early as check that depend
 -# on compiler verison follow below.
 -include(gmxGetCompilerInfo)
 -get_compiler_version()
 -
 -# First exclude compilers known to not work with OpenMP although claim to support it:
 -# gcc 4.2.1 and gcc-llvm 4.2.1 (also claims to be 4.2.1) on Mac OS X
 -# This fixes redmine 900 and needs to run before OpenMP flags are set below.
 -if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND
 -    (CMAKE_COMPILER_IS_GNUCC AND C_COMPILER_VERSION AND C_COMPILER_VERSION VERSION_LESS 4.3))
 -    message(STATUS "OpenMP multithreading not supported with gcc/llvm-gcc 4.2 on Mac OS X, disabled")
 -    set(GMX_OPENMP OFF CACHE BOOL
 -        "OpenMP multithreading not not supported with gcc/llvm-gcc 4.2 on Mac OS X, disabled!" FORCE)
 -endif()
 -
 -# OpenMP check must come before other CFLAGS!
 -if(GMX_OPENMP)
 -    find_package(OpenMP)
 -    if(OPENMP_FOUND)
 -        # CMake on Windows doesn't support linker flags passed to target_link_libraries
 -        # (i.e. it treats /openmp as \openmp library file). Also, no OpenMP linker flags are needed.
 -        if(NOT (WIN32 AND NOT CYGWIN))
 -            if(CMAKE_COMPILER_IS_GNUCC AND GMX_PREFER_STATIC_OPENMP)
 -                set(OpenMP_LINKER_FLAGS "-Wl,-static -lgomp -lrt -Wl,-Bdynamic -lpthread")
 -                set(OpenMP_SHARED_LINKER_FLAGS "")
 -            else()
 -                # Only set a linker flag if the user didn't set them manually
 -                if(NOT DEFINED OpenMP_LINKER_FLAGS)
 -                    set(OpenMP_LINKER_FLAGS "${OpenMP_C_FLAGS}")
 -                endif()
 -                if(NOT DEFINED OpenMP_SHARED_LINKER_FLAGS)
 -                    set(OpenMP_SHARED_LINKER_FLAGS "${OpenMP_C_FLAGS}")
 -                endif()
 -            endif()
 -        endif()
 -    else(OPENMP_FOUND)
 -        message(WARNING
 -                "The compiler you are using does not support OpenMP parallelism. This might hurt your performance a lot, in particular with GPUs. Try using a more recent version, or a different compiler. For now, we are proceeding by turning off OpenMP.")
 -        set(GMX_OPENMP OFF CACHE STRING "Whether GROMACS will use OpenMP parallelism." FORCE)
 -    endif(OPENMP_FOUND)
 -endif()
 +include(CheckCXXCompilerFlag)
  
  
  include(gmxCFlags)
  gmx_c_flags()
  
  # This variable should be used for additional compiler flags which are not
 -# generated in gmxCFlags nor are acceleration or MPI related.
 +# generated in gmxCFlags nor are SIMD or MPI related.
  set(EXTRA_C_FLAGS "")
 +set(EXTRA_CXX_FLAGS "")
  
 -# gcc 4.4.x is buggy and crashes when compiling some files with O3 and OpenMP on.
 -# Detect here whether applying a workaround is needed and will apply it later
 -# on the affected files.
 -include(gmxGCC44O3BugWorkaround)
 -gmx_check_gcc44_bug_workaround_needed(GMX_USE_GCC44_BUG_WORKAROUND)
 -
 -# clang 3.0 is buggy for some unknown reason detected during adding
 -# the SSE2 group kernels for GROMACS 4.6. If we ever work out what
 -# that is, we should replace these tests with a compiler feature test,
 -# update GROMACS Redmine task #1039 and perhaps report a clang bug.
 -#
 -# In the meantime, until we require CMake 2.8.10 we cannot rely on it to detect
 -# the compiler version for us. So we need a manual check for clang 3.0.
 -include(gmxDetectClang30)
 -gmx_detect_clang_3_0(COMPILER_IS_CLANG_3_0)
 -if(COMPILER_IS_CLANG_3_0)
 -    message(FATAL_ERROR "Your compiler is clang version 3.0, which is known to be buggy for GROMACS. Use a different compiler.")
 -endif()
 -
 -# clang <=3.2 contains a bug that causes incorrect code to be generated for the
 -# vfmaddps instruction and therefore the bug is triggered with AVX_128_FMA.
 -# (see: http://llvm.org/bugs/show_bug.cgi?id=15040).
 -# We can work around this by not using the integrated assembler (except on OS X
 -# which has an outdated assembler that does not support AVX instructions).
 -if (${CMAKE_C_COMPILER_ID} MATCHES "Clang" AND C_COMPILER_VERSION VERSION_LESS "3.3")
 -    set(GMX_USE_CLANG_FMA_BUG_WORKAROUND TRUE)
 -endif()
 -
 -# GMX_CPU_ACCELERATION will not be set automatically until the second
 +# Run through a number of tests for buggy compilers and other issues
 +include(gmxTestCompilerProblems)
 +gmx_test_compiler_problems()
++# GMX_SIMD will not be set automatically until the second
+ # pass (which is not strictly guaranteed to occur), so putting this
+ # check here among logically-related tests is inefficient, but the
+ # potential loss is likely zero.
 -if(GMX_CPU_ACCELERATION STREQUAL "AVX_256"
++if(GMX_SIMD STREQUAL "AVX_256"
+         AND CMAKE_COMPILER_IS_GNUCC
+         AND (C_COMPILER_VERSION VERSION_EQUAL "4.6.1"
+             OR CXX_COMPILER_VERSION VERSION_EQUAL "4.6.1"))
 -    message(FATAL_ERROR "gcc 4.6.1 has buggy support for AVX, and GROMACS mdrun will not work. If you want simulation performance, use a more recent compiler. Otherwise, use GMX_CPU_ACCELERATION=SSE4.1")
++    message(FATAL_ERROR "gcc 4.6.1 has buggy support for AVX, and GROMACS mdrun will not work. If you want simulation performance, use a more recent compiler. Otherwise, use GMX_SIMD=SSE4.1")
+     # See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49002
+ endif()
 -if (CMAKE_C_COMPILER_ID STREQUAL "PGI")
 -    message(WARNING "All tested PGI compiler versions (up to 12.9.0) generate binaries which produce incorrect results, or even fail to compile Gromacs. Highly recommended to use a different compiler. If you choose to use PGI, make sure to run the regressiontests.")
 -endif()
  
 -if(CMAKE_C_COMPILER_ID MATCHES "Intel" AND C_COMPILER_VERSION VERSION_LESS "12.0.0")
 -    message(WARNING "Intel compilers before 12.0.0 are not routinely tested, so there may be problems. Version 11.1 with SSE4.1 is known to produce incorrect results. It is highly recommended to use a more up-to-date compiler. If you choose to use this version, make sure you run the regressiontests.")
 -endif()
  
  set(PKG_CFLAGS "")
  if(GMX_DOUBLE)
 +    add_definitions(-DGMX_DOUBLE)
      set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_DOUBLE")
 -endif(GMX_DOUBLE)
 +endif()
  if(GMX_SOFTWARE_INVSQRT)
    set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_SOFTWARE_INVSQRT")
 -endif(GMX_SOFTWARE_INVSQRT)
 +endif()
 +
 +if(WIN32 AND NOT CYGWIN)
 +    set(GMX_WSOCKLIB_PATH CACHE PATH "Path to winsock (wsock32.lib) library.")
 +    mark_as_advanced(GMX_WSOCKLIB_PATH)
 +    find_library(WSOCK32_LIBRARY NAMES wsock32 PATHS ${GMX_WSOCKLIB_PATH})
 +    if(WSOCK32_LIBRARY)
 +        list(APPEND GMX_EXTRA_LIBRARIES ${WSOCK32_LIBRARY})
 +        add_definitions(-DGMX_HAVE_WINSOCK)
 +    else()
 +        message(STATUS "No winsock found. Cannot use interactive molecular dynamics (IMD).")
 +    endif(WSOCK32_LIBRARY)
 +endif()
  
  
  
  # Basic system tests (standard libraries, headers, functions, types)   #
  ########################################################################
  include(CheckIncludeFiles)
 -check_include_files(string.h     HAVE_STRING_H)
 -check_include_files(math.h       HAVE_MATH_H)
 -check_include_files(limits.h     HAVE_LIMITS_H)
 -check_include_files(memory.h     HAVE_MEMORY_H)
 -check_include_files(unistd.h   HAVE_UNISTD_H)
 -check_include_files(direct.h   HAVE_DIRECT_H)
 +include(CheckIncludeFileCXX)
 +check_include_files(unistd.h     HAVE_UNISTD_H)
  check_include_files(pwd.h        HAVE_PWD_H)
 -check_include_files(stdint.h   HAVE_STDINT_H)
 -check_include_files(stdlib.h   HAVE_STDLIB_H)
 -check_include_files(pthread.h    HAVE_PTHREAD_H)
  check_include_files(dirent.h     HAVE_DIRENT_H)
 -check_include_files(inttypes.h   HAVE_INTTYPES_H)
 -check_include_files(regex.h      HAVE_REGEX_H)
 -check_include_files(sys/types.h  HAVE_SYS_TYPES_H)
 -check_include_files(sys/stat.h   HAVE_SYS_STAT_H)
 +check_include_files(time.h       HAVE_TIME_H)
  check_include_files(sys/time.h   HAVE_SYS_TIME_H)
 -check_include_files(rpc/rpc.h    HAVE_RPC_RPC_H)
 -check_include_files("rpc/rpc.h;rpc/xdr.h"    HAVE_RPC_XDR_H)
 -check_include_files(io.h               HAVE_IO_H)
 +check_include_files(io.h         HAVE_IO_H)
  check_include_files(sched.h      HAVE_SCHED_H)
  
 +check_include_files(regex.h      HAVE_POSIX_REGEX)
 +check_include_file_cxx(regex     HAVE_CXX11_REGEX)
 +# TODO: It could be nice to inform the user if no regex support is found,
 +# as selections won't be fully functional.
 +
  include(CheckFunctionExists)
 -check_function_exists(strcasecmp        HAVE_STRCASECMP)
 -check_function_exists(strdup            HAVE_STRDUP)
 -check_function_exists(vprintf           HAVE_VPRINTF)
 -check_function_exists(memcmp            HAVE_MEMCMP)
  check_function_exists(posix_memalign    HAVE_POSIX_MEMALIGN)
  check_function_exists(memalign          HAVE_MEMALIGN)
  check_function_exists(_aligned_malloc   HAVE__ALIGNED_MALLOC)
  check_function_exists(gettimeofday      HAVE_GETTIMEOFDAY)
 -check_function_exists(isnan             HAVE_ISNAN)
 -check_function_exists(_isnan            HAVE__ISNAN)
  check_function_exists(fsync             HAVE_FSYNC)
  check_function_exists(_fileno           HAVE__FILENO)
  check_function_exists(fileno            HAVE_FILENO)
@@@ -385,11 -392,70 +397,11 @@@ check_function_exists(sqrt
  
  include(CheckLibraryExists)
  check_library_exists(m sqrt "" HAVE_LIBM)
 -check_library_exists(m cbrt "" HAVE_CBRT)
 -
 -
 -include(CheckTypeSize)
 -
 -check_type_size("bool"          SIZEOF_BOOL) # will also set HAVE_BOOL
 -check_type_size("int"           SIZEOF_INT) 
 -check_type_size("long int"      SIZEOF_LONG_INT) 
 -check_type_size("long long int" SIZEOF_LONG_LONG_INT) 
 -check_type_size("off_t"         SIZEOF_OFF_T)
 -check_type_size("void *"        SIZEOF_VOIDP)
 -
 -if (CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
 -    set(GMX_64_BIT TRUE)
 -else (CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
 -    set(GMX_64_BIT FALSE)
 -endif (CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
 -
 -# Check for some basic types that we *need*, so set these to int if they are not present 
 -check_type_size(uid_t uid_t)
 -if(NOT uid_t)
 -  set(uid_t int)
 -else(NOT uid_t)
 -  set(uid_t 0)
 -endif(NOT uid_t)
 -
 -check_type_size(gid_t gid_t)
 -if(NOT gid_t)
 -  set(gid_t 1)
 -else(NOT gid_t)
 -  set(gid_t 0)
 -endif(NOT gid_t)
 -
 -check_type_size(size_t size_t)
 -if(NOT size_t)
 -  set(size_t int)
 -else(NOT size_t)
 -  set(size_t 0)
 -endif(NOT size_t)
 -
 -check_type_size(off_t off_t)
 -if(NOT off_t)
 -  set(off_t int)
 -else(NOT off_t)
 -  set(off_t 0)
 -endif(NOT off_t)
 +check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME)
  
  include(TestBigEndian)
  test_big_endian(GMX_INTEGER_BIG_ENDIAN)
  
 -
 -if(APPLE OR CYGWIN OR ${CMAKE_SYSTEM_NAME} MATCHES "Linux|.*BSD|GNU")
 -    # Maybe Solaris should be here? Patch this if you know!
 -    SET(SHARED_LIBS_DEFAULT ON)
 -elseif(WIN32 OR ${CMAKE_SYSTEM_NAME} MATCHES "BlueGene")
 -    # Support for shared libs on native Windows is a bit new. Its
 -    # default might change later if/when we sort things out. Also,
 -    # Cray should go here. What variable value can detect it?
 -    SET(SHARED_LIBS_DEFAULT OFF)
 -else()
 -    message(STATUS "Defaulting to building static libraries")
 -    SET(SHARED_LIBS_DEFAULT OFF)
 -endif()
 -
  # Management of GROMACS options for specific toolchains should go
  # here. Because the initial settings for some of the main options have
  # already happened, but things like library detection and MPI compiler
@@@ -401,149 -467,139 +413,149 @@@ if(${CMAKE_SYSTEM_NAME} MATCHES BlueGen
      include(gmxManageBlueGene)
  endif()
  
 -if(UNIX AND GMX_PREFER_STATIC_LIBS AND SHARED_LIBS_DEFAULT)
 -    if(BUILD_SHARED_LIBS)
 -        # Warn the user about the combination. But don't overwrite the request.
 -        message(WARNING "Searching for static libraries requested, and building shared Gromacs libraries requested. This might cause problems linking later.")
 -    elseif(NOT DEFINED BUILD_SHARED_LIBS)
 -        # Change default to OFF. Don't warn if it's already off.
 -        message(WARNING "Searching for static libraries requested, so the GROMACS libraries will also be built statically (BUILD_SHARED_LIBS=OFF)")
 -        set(SHARED_LIBS_DEFAULT OFF)
 -    endif()
 -endif()
 -
 -# By now, all tool chains should have spoken up if they care about
 -# the setting of SHARED_LIBS_DEFAULT.
 -option(BUILD_SHARED_LIBS "Enable shared libraries (can be problematic e.g. with MPI, or on some HPC systems)" ${SHARED_LIBS_DEFAULT})
 -
  ########################################################################
  #Process MPI settings
  ########################################################################
  include(gmxManageMPI)
  
 +
 +########################################################################
 +#Process shared/static library settings
 +########################################################################
 +include(gmxManageSharedLibraries)
 +
 +
  ########################################################################
  # Find external packages                                               #
  ########################################################################
 -if(UNIX AND GMX_PREFER_STATIC_LIBS)
 -    # On Linux .a is the static library suffix, on Mac OS X .lib can also
 -    # be used, so we'll add both to the preference list.
 -    SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib;.a" ${CMAKE_FIND_LIBRARY_SUFFIXES})
 -endif()
  
 -IF( WIN32 AND NOT CYGWIN)
 -  if (NOT BUILD_SHARED_LIBS)
 -      option(GMX_PREFER_STATIC_LIBS "When finding libraries prefer static system libraries (MT instead of MD)!" ON)
 -      if(NOT GMX_PREFER_STATIC_LIBS)
 -          message(WARNING "Shared system libraries requested, and static Gromacs libraries requested.")
 -      endif()
 +# Unconditionally find the package, as it is also required for unit
 +# tests. This exports LIBXML2_FOUND, which we should not use because
 +# it does not tell us that linking will succeed. Instead, we test that
 +# next.
 +find_package(LibXml2)
 +include(gmxTestLibXml2)
 +gmx_test_libxml2(HAVE_LIBXML2)
 +option(GMX_XML "Use libxml2 to parse xml files (currently has no effect)" ${HAVE_LIBXML2})
 +set(PKG_XML "")
 +mark_as_advanced(GMX_XML)
 +# Don't actually do anything, since libxml2 is currently not used by libgromacs
 +#if(GMX_XML AND NOT HAVE_LIBXML2)
 +#    message(FATAL_ERROR "libxml2 not found. Set GMX_XML=OFF to compile without XML support")
 +#endif()
 +#if(GMX_XML)
 +#    include_directories(${LIBXML2_INCLUDE_DIR})
 +#    set(PKG_XML libxml-2.0)
 +#    set(XML_LIBRARIES ${LIBXML2_LIBRARIES})
 +#endif()
 +
 +option(GMX_EXTRAE "Add support for tracing using EXTRAE" OFF)
 +mark_as_advanced(GMX_EXTRAE)
 +
 +if (GMX_EXTRAE)
 +  find_package(EXTRAE)
 +  if(EXTRAE_FOUND)
 +    include_directories(${EXTRAE_INCLUDE_DIR})
 +    set(HAVE_EXTRAE 1)
    else()
 -      option(GMX_PREFER_STATIC_LIBS "When finding libraries prefer static system libraries (MT instead of MD)!" OFF)
 -      if(GMX_PREFER_STATIC_LIBS)
 -          #this combination segfaults (illigal passing of file handles)
 -          message(FATAL_ERROR "Static system libraries requested, and shared Gromacs libraries requested.")
 -      endif()
 -      add_definitions(-DUSE_VISIBILITY -DTMPI_USE_VISIBILITY)
 -      set(PKG_CFLAGS "$PKG_CFLAGS -DUSE_VISIBILITY -DTMPI_USE_VISIBILITY")
 +    message(FATAL_ERROR "EXTRAE library was not found. Please add the correct path to CMAKE_PREFIX_PATH")
    endif()
 -  mark_as_advanced(GMX_PREFER_STATIC_LIBS)
 -
 -  IF (GMX_PREFER_STATIC_LIBS)
 -      #Only setting Debug and Release flags. Others configurations are current not used.
 -      STRING(REPLACE /MD /MT CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
 -      STRING(REPLACE /MD /MT CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
 -      if(CMAKE_CXX_COMPILER_LOADED)
 -          STRING(REPLACE /MD /MT CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
 -          STRING(REPLACE /MD /MT CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
 -      endif()
 -  ENDIF()
 -  IF( CMAKE_C_COMPILER_ID MATCHES "Intel" )
 -    if(BUILD_SHARED_LIBS) #not sure why incremental building with shared libs doesn't work
 -        STRING(REPLACE "/INCREMENTAL:YES" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS})
 -    endif()
 -  ENDIF()
 -ENDIF()
 -
 -option(GMX_GSL "Add support for gsl" OFF)
 -if (GMX_GSL)
 -  find_package(GSL)
 -  set(PKG_GSL "")
 -  if(GSL_FOUND)
 -    include_directories(${GSL_INCLUDE_DIR})
 -    set(PKG_GSL gsl)
 -    set(HAVE_LIBGSL 1)
 -  endif(GSL_FOUND)
 -endif (GMX_GSL)
 +endif()
  
  option(GMX_X11 "Use X window system" OFF)
  if (GMX_X11)
      find_package(X11)
      # X11 includes/libraries are only set in the ngmx subdirectory!
      if(NOT X11_FOUND)
 -        message(WARNING "X11 include files and/or libraries were not found. Will not build the GROMACS X11-binaries, such as ngmx")
 +        message(FATAL_ERROR
 +                "X11 include files and/or libraries were not found. "
 +                "Set GMX_X11=OFF to compile without X11 support. "
 +                "gmx view will not be available.")
      endif()
 -endif(GMX_X11)
 +    include_directories(${X11_INCLUDE_DIR})
 +endif()
  
  include(ThreadMPI)
 -set(THREAD_MPI_LIB thread_mpi)
  # Enable core threading facilities
 -tmpi_enable_core("${CMAKE_SOURCE_DIR}/include")
 +tmpi_enable_core("${CMAKE_SOURCE_DIR}/src/external/thread_mpi/include")
  # Enable tMPI C++ support
 -tmpi_enable_cxx()
 +tmpi_enable_cxx()
  if(GMX_THREAD_MPI)
      # enable MPI functions
      tmpi_enable()
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_THREAD_MPI")
      set(GMX_MPI 1)
 -endif(GMX_THREAD_MPI)
 -tmpi_get_source_list(THREAD_MPI_SRC)
 +    set(MPI_IN_PLACE_EXISTS 1)
 +endif()
 +# If atomics are manually disabled a define is needed because atomics.h doesn't depend on config.h
 +if (TMPI_ATOMICS_DISABLED)
 +   add_definitions(-DTMPI_ATOMICS_DISABLED)
 +endif()
  
  if(GMX_GPU)
      # now that we have detected the dependencies, do the second configure pass
      gmx_gpu_setup()
 -endif(GMX_GPU)
 +endif()
  
  if(CYGWIN)
      set(GMX_CYGWIN 1)
 -endif(CYGWIN)
 +endif()
  
  if(WIN32 AND NOT CYGWIN)
      set(GMX_NATIVE_WINDOWS 1)
 +    # This makes windows.h not declare min/max as macros that would break
 +    # C++ code using std::min/std::max.
 +    add_definitions(-DNOMINMAX)
  endif()
  
  # only bother with finding git and using version.h if the source is a git repo
 -if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
 -    if(USE_VERSION_H)
 -        # We need at least git v1.5.3 be able to parse git's date output. If not
 -        # found or the version is too small, we can't generate version information.
 -        find_package(Git)
 -
 -      # Find out the git version
 -      if(GIT_FOUND AND NOT GIT_VERSION)
 -        execute_process(COMMAND ${GIT_EXECUTABLE} "--version"
 -            OUTPUT_VARIABLE _exec_out
 -            OUTPUT_STRIP_TRAILING_WHITESPACE)
 -        string(REGEX REPLACE "git version (.*)" "\\1" GIT_VERSION ${_exec_out})
 -        set(GIT_VERSION ${GIT_VERSION} CACHE STRING "Git version")
 -        mark_as_advanced(GIT_VERSION)
 -      endif()
 -
 -        if(NOT GIT_FOUND OR GIT_VERSION VERSION_LESS "1.5.3")
 -          message("No compatible git version found, won't be able to generate proper development version information.")
 -          set(USE_VERSION_H OFF)
 -        endif()
 +if(GMX_GIT_VERSION_INFO)
 +    if (NOT SOURCE_IS_GIT_REPOSITORY)
 +        message(FATAL_ERROR
 +            "Cannot generate git version information from source tree not under git. "
 +            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
      endif()
 -else()
 -    set(USE_VERSION_H OFF)
 +    # We need at least git v1.5.3 be able to parse git's date output. If not
 +    # found or the version is too small, we can't generate version information.
 +    find_package(Git)
 +
 +    if(NOT GIT_FOUND OR GIT_VERSION_STRING VERSION_LESS "1.5.3")
 +        message(FATAL_ERROR
 +            "No compatible git version found (>= 1.5.3 required). "
 +            "Won't be able to generate development version information. "
 +            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 +    endif()
 +endif()
 +
 +# Detect boost unless GMX_EXTERNAL_BOOST is explicitly OFF
 +# Used for default if GMX_EXTERNAL_BOOST is not defined (first CMake pass)
 +if(NOT DEFINED GMX_EXTERNAL_BOOST OR GMX_EXTERNAL_BOOST)
 +    find_package(Boost 1.44.0)
 +    if(Boost_FOUND AND Boost_VERSION VERSION_LESS "104400")
 +        set(Boost_FOUND FALSE)
 +    endif()
 +    # Print the notification only on first run, when determining the default
 +    if(NOT DEFINED GMX_EXTERNAL_BOOST AND NOT Boost_FOUND)
 +        message("Boost >= 1.44 not found. Using minimal internal version. "
 +                "This may cause trouble if you plan on compiling/linking other "
 +                "software that uses Boost against Gromacs.")
 +    endif()
 +endif()
 +option(GMX_EXTERNAL_BOOST "Use external Boost instead of minimal built-in version"
 +       ${Boost_FOUND})
 +if(GMX_EXTERNAL_BOOST AND NOT Boost_FOUND)
 +    message(FATAL_ERROR
 +        "Boost >= 1.44 not found. "
 +        "You can set GMX_EXTERNAL_BOOST=OFF to compile against minimal "
 +        "version of Boost included with Gromacs.")
 +endif()
 +
 +option(GMX_BUILD_UNITTESTS "Build unit tests with BUILD_TESTING (uses Google C++ Testing and Mocking Frameworks, requires libxml2)" ${HAVE_LIBXML2})
 +mark_as_advanced(GMX_BUILD_UNITTESTS)
 +gmx_add_cache_dependency(GMX_BUILD_UNITTESTS BOOL BUILD_TESTING OFF)
 +if (GMX_BUILD_UNITTESTS AND NOT HAVE_LIBXML2)
 +    message(FATAL_ERROR
 +        "Cannot build unit tests without libxml2. "
 +        "Either set GMX_BUILD_UNITTESTS=OFF or tell CMake how to find a working version of libxml2.")
  endif()
  
  ########################################################################
  ########################################################################
  
  add_definitions( -DHAVE_CONFIG_H )
 +include_directories(${CMAKE_SOURCE_DIR}/src)
 +include_directories(${CMAKE_SOURCE_DIR}/src/external/thread_mpi/include)
 +# Required for config.h, maybe should only be set in src/CMakeLists.txt
  include_directories(${CMAKE_BINARY_DIR}/src)
 -include_directories(${CMAKE_BINARY_DIR}/include)
 -include_directories(${CMAKE_SOURCE_DIR}/include)
 +# Required for gmx_header_config_gen.h to be found before installation
 +include_directories(${CMAKE_BINARY_DIR}/src/gromacs/utility)
 +# Required for now to make old code compile
 +include_directories(${CMAKE_SOURCE_DIR}/src/gromacs/legacyheaders)
  
  include(gmxTestInlineASM)
  gmx_test_inline_asm_gcc_x86(GMX_X86_GCC_INLINE_ASM)
@@@ -575,12 -626,12 +587,12 @@@ gmx_set_build_information(
  # - the build system's CPU supports it
  # - the acceleration is set to AVX as all AVX-capable CPUs support AVX (which
  #   at this point means that the user set it).
 -# Note: it's better to not use the later set value of GMX_CPU_ACCELERATION because
 +# Note: it's better to not use the later set value of GMX_SIMD because
  # it reflects the system's capability of both compiling and running AVX code.
  # TODO: After merge with 5.0 one could implement a cache variable dependency
  # such that GMX_USE_RDTSCP can change if GMX_CPU_ACCELERATION is changed to AVX
  # after the first cmake pass.
 -if (BUILD_CPU_FEATURES MATCHES "rdtscp" OR GMX_CPU_ACCELERATION MATCHES "AVX")
 +if (BUILD_CPU_FEATURES MATCHES "rdtscp" OR GMX_SIMD MATCHES "AVX")
      set(GMX_USE_RDTSCP_DEFAULT_VALUE ON)
  else()
      set(GMX_USE_RDTSCP_DEFAULT_VALUE OFF)
@@@ -588,11 -639,12 +600,11 @@@ endif(
  option(GMX_USE_RDTSCP "Use RDTSCP for better CPU-based timers (available on recent x86 CPUs; might need to be off when compiling for heterogeneous environments)" ${GMX_USE_RDTSCP_DEFAULT_VALUE})
  mark_as_advanced(GMX_USE_RDTSCP)
  if(GMX_USE_RDTSCP)
 -    # The timestep counter headers do not include config.h
 -    add_definitions(-DHAVE_RDTSCP)
 +    set(HAVE_RDTSCP 1)
  endif()
  
  include(gmxTestFloatFormat)
 -gmx_test_float_format(GMX_FLOAT_FORMAT_IEEE754 
 +gmx_test_float_format(GMX_FLOAT_FORMAT_IEEE754
                        GMX_IEEE754_BIG_ENDIAN_BYTE_ORDER
                        GMX_IEEE754_BIG_ENDIAN_WORD_ORDER)
  
@@@ -602,6 -654,12 +614,6 @@@ gmx_test_large_files(GMX_LARGEFILES
  include(gmxTestSignal)
  gmx_test_sigusr1(HAVE_SIGUSR1)
  
 -include(gmxTestInline)
 -gmx_test_inline(INLINE_KEYWORD)
 -
 -include(gmxTestRestrict)
 -gmx_test_restrict(RESTRICT_KEYWORD)
 -
  include(gmxTestPipes)
  gmx_test_pipes(HAVE_PIPES)
  
@@@ -610,38 -668,225 +622,38 @@@ gmx_test_isfinite(HAVE_ISFINITE
  gmx_test__isfinite(HAVE__ISFINITE)
  gmx_test__finite(HAVE__FINITE)
  
 +include(gmxTestCXX11)
 +gmx_test_cxx11(GMX_CXX11_SUPPORTED GMX_CXX11_FLAGS)
 +include(CMakeDependentOption)
 +# nvcc does not support C++11 flags, so with GPUs we prefer to skip C++11 flags
 +# entirely to keep the compilation environment uniform.
 +cmake_dependent_option(GMX_CXX11
 +    "Use C++11 features"
 +    ON "GMX_CXX11_SUPPORTED AND NOT GMX_GPU" OFF)
 +mark_as_advanced(GMX_CXX11)
 +if(GMX_CXX11)
 +    set(EXTRA_CXX_FLAGS "${EXTRA_CXX_FLAGS} ${GMX_CXX11_FLAGS}")
 +endif()
 +
 +
  include(gmxTestXDR)
  gmx_test_xdr(GMX_SYSTEM_XDR)
  if(NOT GMX_SYSTEM_XDR)
      set(GMX_INTERNAL_XDR 1)
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_INTERNAL_XDR")
 -endif(NOT GMX_SYSTEM_XDR)
 -
 -# include avx test source, used if the AVX flags are set below
 -include(gmxTestAVXMaskload)
 -
 -# Process nonbonded accelerated kernels settings
 -#
 -# Note that for the backward-compatible x86 SIMD architectures, the
 -# GMX_CPU_ACCELERATION determines the maximum level of the instruction
 -# set used (e.g. GMX_CPU_ACCLERATION=SSE4.1 implies
 -# SSE2). Accordingly, there are a set of CMake variables
 -# GMX_<arch>_<feature-set> that are exported to the C code to specify
 -# CPU features that should be used. This means that the logic for
 -# requiring such backward compatibility is all located here.
 -string(TOUPPER ${GMX_CPU_ACCELERATION} GMX_CPU_ACCELERATION)
 -if(${GMX_CPU_ACCELERATION} STREQUAL "NONE")
 -    # nothing to do
 -elseif(${GMX_CPU_ACCELERATION} STREQUAL "SSE2")
 -
 -    GMX_TEST_CFLAG(GNU_SSE2_CFLAG "-msse2" ACCELERATION_C_FLAGS)
 -    if(NOT GNU_SSE2_CFLAG AND GMX_NATIVE_WINDOWS)
 -        GMX_TEST_CFLAG(MSVC_SSE2_CFLAG "/arch:SSE2" ACCELERATION_C_FLAGS)
 -    endif(NOT GNU_SSE2_CFLAG AND GMX_NATIVE_WINDOWS)
 -
 -    if (CMAKE_CXX_COMPILER_LOADED)
 -        GMX_TEST_CXXFLAG(GNU_SSE2_CXXFLAG "-msse2" ACCELERATION_CXX_FLAGS)
 -        if(NOT GNU_SSE2_CXXFLAG AND GMX_NATIVE_WINDOWS)
 -            GMX_TEST_CXXFLAG(MSVC_SSE2_CXXFLAG "/arch:SSE2" ACCELERATION_CXX_FLAGS)
 -        endif(NOT GNU_SSE2_CXXFLAG AND GMX_NATIVE_WINDOWS)
 -    endif()
 -
 -    # We dont warn for lacking SSE2 flag support, since that is probably standard today.
 -
 -    # Only test the include after we have tried to add the correct flag for SSE2 support
 -    check_include_file(emmintrin.h  HAVE_EMMINTRIN_H ${ACCELERATION_C_FLAGS})
 -
 -    if(NOT HAVE_EMMINTRIN_H)
 -        message(FATAL_ERROR "Cannot find emmintrin.h, which is required for SSE2 intrinsics support.")
 -    endif(NOT HAVE_EMMINTRIN_H)
 -
 -    set(GMX_CPU_ACCELERATION_X86_SSE2 1)
 -    # The user should not be able to set this orthogonally to the acceleration
 -    set(GMX_X86_SSE2 1)
 -    if (NOT ACCELERATION_QUIETLY)
 -      message(STATUS "Enabling SSE2 Gromacs acceleration")
 -    endif()
 -
 -elseif(${GMX_CPU_ACCELERATION} STREQUAL "SSE4.1")
 -
 -    GMX_TEST_CFLAG(GNU_SSE4_CFLAG "-msse4.1" ACCELERATION_C_FLAGS)
 -    if (NOT GNU_SSE4_CFLAG AND GMX_NATIVE_WINDOWS)
 -        GMX_TEST_CFLAG(MSVC_SSE4_CFLAG "/arch:SSE4.1" ACCELERATION_C_FLAGS)
 -    endif(NOT GNU_SSE4_CFLAG AND GMX_NATIVE_WINDOWS)
 -    if (NOT GNU_SSE4_CFLAG AND NOT MSVC_SSE4_CFLAG)
 -        # Not surprising if we end up here! MSVC current does not support the SSE4.1 flag. However, it appears to accept SSE4.1
 -        # intrinsics when SSE2 support is enabled, so we try that instead first.
 -      if (GMX_NATIVE_WINDOWS)
 -            GMX_TEST_CFLAG(MSVC_SSE2_CFLAG "/arch:SSE2" ACCELERATION_C_FLAGS)
 -            message(WARNING "Neither SSE4.1 or SSE2 seems to be supported by your Windows compiler. Something is likely broken.")
 -        else()
 -            message(WARNING "No C SSE4.1 flag found. Consider a newer compiler, or use SSE2 for slightly lower performance")
 -        endif()
 -    endif(NOT GNU_SSE4_CFLAG AND NOT MSVC_SSE4_CFLAG)
 -
 -    if (CMAKE_CXX_COMPILER_LOADED)
 -        GMX_TEST_CXXFLAG(GNU_SSE4_CXXFLAG "-msse4.1" ACCELERATION_CXX_FLAGS)
 -        if (NOT GNU_SSE4_CXXFLAG AND GMX_NATIVE_WINDOWS)
 -            GMX_TEST_CXXFLAG(MSVC_SSE4_CXXFLAG "/arch:SSE4.1" ACCELERATION_CXX_FLAGS)
 -        endif(NOT GNU_SSE4_CXXFLAG AND GMX_NATIVE_WINDOWS)
 -        if (NOT GNU_SSE4_CXXFLAG AND NOT MSVC_SSE4_CXXFLAG) 
 -            message(WARNING "No C++ SSE4.1 flag found. Consider a newer compiler, or use SSE2 for slightly lower performance.")
 -            # Not surprising if we end up here! MSVC current does not support the SSE4.1 flag. However, it appears to accept SSE4.1
 -            # intrinsics when SSE2 support is enabled, so we try that instead.
 -            if (GMX_NATIVE_WINDOWS)
 -                GMX_TEST_CXXFLAG(MSVC_SSE2_CXXFLAG "/arch:SSE2" ACCELERATION_CXX_FLAGS)
 -            endif()
 -        endif(NOT GNU_SSE4_CXXFLAG AND NOT MSVC_SSE4_CXXFLAG)
 -    endif()
 -
 -    # This must come after we have added the -msse4.1 flag on some platforms.
 -    check_include_file(smmintrin.h  HAVE_SMMINTRIN_H ${ACCELERATION_C_FLAGS})
 -
 -    if(NOT HAVE_SMMINTRIN_H)
 -        message(FATAL_ERROR "Cannot find smmintrin.h, which is required for SSE4.1 intrinsics support.")
 -    endif(NOT HAVE_SMMINTRIN_H)
 -
 -    set(GMX_CPU_ACCELERATION_X86_SSE4_1 1)
 -    # The user should not be able to set this orthogonally to the acceleration
 -    set(GMX_X86_SSE4_1 1)
 -    set(GMX_X86_SSE2   1)
 -    if (NOT ACCELERATION_QUIETLY)
 -      message(STATUS "Enabling SSE4.1 Gromacs acceleration")
 -    endif()
 -
 -    if(CMAKE_C_COMPILER_ID MATCHES "Intel" AND C_COMPILER_VERSION VERSION_EQUAL "11.1")
 -        message(FATAL_ERROR "You are using Intel compiler version 11.1, and that compiler is known to produce incorrect results with SSE4.1 acceleration. You need to use another compiler (e.g. icc 12 or newer) or different acceleration (probably slower simulations).")
 -    endif()
 -elseif(${GMX_CPU_ACCELERATION} STREQUAL "AVX_128_FMA" OR ${GMX_CPU_ACCELERATION} STREQUAL "AVX_256")
 -
 -    # Set the AVX compiler flag for both these choices!
 -
 -    GMX_TEST_CFLAG(GNU_AVX_CFLAG "-mavx" ACCELERATION_C_FLAGS)
 -    if (NOT GNU_AVX_CFLAG AND GMX_NATIVE_WINDOWS)
 -        GMX_TEST_CFLAG(MSVC_AVX_CFLAG "/arch:AVX" ACCELERATION_C_FLAGS)
 -    endif (NOT GNU_AVX_CFLAG AND GMX_NATIVE_WINDOWS)
 -    if (NOT GNU_AVX_CFLAG AND NOT MSVC_AVX_CFLAG)
 -        message(WARNING "No C AVX flag found. Consider a newer compiler, or try SSE4.1 (lower performance) giving the -DGMX_CPU_ACCELERATION=SSE4.1 to cmake.")
 -    endif (NOT GNU_AVX_CFLAG AND NOT MSVC_AVX_CFLAG)
 -
 -    if (CMAKE_CXX_COMPILER_LOADED)
 -        GMX_TEST_CXXFLAG(GNU_AVX_CXXFLAG "-mavx" ACCELERATION_CXX_FLAGS)
 -        if (NOT GNU_AVX_CXXFLAG AND GMX_NATIVE_WINDOWS)
 -            GMX_TEST_CXXFLAG(MSVC_AVX_CXXFLAG "/arch:AVX" ACCELERATION_CXX_FLAGS)
 -        endif (NOT GNU_AVX_CXXFLAG AND GMX_NATIVE_WINDOWS)
 -        if (NOT GNU_AVX_CXXFLAG AND NOT MSVC_AVX_CXXFLAG)
 -            message(WARNING "No C++ AVX flag found. Consider a newer compiler, or try SSE4.1 (lower performance) giving the -DGMX_CPU_ACCELERATION=SSE4.1 to cmake.")
 -        endif (NOT GNU_AVX_CXXFLAG AND NOT MSVC_AVX_CXXFLAG)
 -    endif()
 -
 -    # Set the FMA4 flags (MSVC doesn't require any)
 -    if(${GMX_CPU_ACCELERATION} STREQUAL "AVX_128_FMA" AND NOT MSVC)
 -        GMX_TEST_CFLAG(GNU_FMA_CFLAG "-mfma4" ACCELERATION_C_FLAGS)
 -        if (NOT GNU_FMA_CFLAG)
 -            message(WARNING "No C FMA4 flag found. Consider a newer compiler, or try SSE4.1 (lower performance).")
 -        endif(NOT GNU_FMA_CFLAG)
 -        GMX_TEST_CFLAG(GNU_XOP_CFLAG "-mxop" ACCELERATION_C_FLAGS)
 -        # No big deal if we do not have xop, so no point yelling warnings about it.
 -        if (CMAKE_CXX_COMPILER_LOADED)
 -            GMX_TEST_CXXFLAG(GNU_FMA_CXXFLAG "-mfma4" ACCELERATION_CXX_FLAGS)
 -            if (NOT GNU_FMA_CXXFLAG)
 -                message(WARNING "No C++ FMA flag found. Consider a newer compiler, or try SSE4.1 (lower performance).")
 -            endif (NOT GNU_FMA_CXXFLAG)
 -            GMX_TEST_CXXFLAG(GNU_XOP_CXXFLAG "-mxop" ACCELERATION_CXX_FLAGS)
 -            # No big deal if we do not have xop, so no point yelling warnings about it.
 -        endif()
 -    endif()
 -
 -    # Only test the header after we have tried to add the flag for AVX support
 -    check_include_file(immintrin.h  HAVE_IMMINTRIN_H ${ACCELERATION_C_FLAGS})
 -
 -    if(NOT HAVE_IMMINTRIN_H)
 -        message(FATAL_ERROR "Cannot find immintrin.h, which is required for AVX intrinsics support. Consider switching compiler.")
 -    endif(NOT HAVE_IMMINTRIN_H)
 +endif()
  
 -    if(${GMX_CPU_ACCELERATION} STREQUAL "AVX_256")
 -        try_compile(TEST_AVX ${CMAKE_BINARY_DIR}
 -            "${CMAKE_SOURCE_DIR}/cmake/TestAVX.c"
 -            COMPILE_DEFINITIONS "${ACCELERATION_C_FLAGS}")
 -        if(NOT TEST_AVX)
 -            message(FATAL_ERROR "Cannot compile AVX intrinsics. Consider switching compiler.")
 -        endif()
 -    endif()
  
 -    # GCC requires x86intrin.h for FMA support. MSVC 2010 requires intrin.h for FMA support.
 -    check_include_file(x86intrin.h HAVE_X86INTRIN_H ${ACCELERATION_C_FLAGS})
 -    check_include_file(intrin.h HAVE_INTRIN_H ${ACCELERATION_C_FLAGS})
 -
 -    # The user should not be able to set this orthogonally to the acceleration
 -    set(GMX_X86_SSE4_1 1)
 -    set(GMX_X86_SSE2   1)
 -
 -    # But just enable one of the choices internally...
 -    if(${GMX_CPU_ACCELERATION} STREQUAL "AVX_128_FMA")
 -        set(GMX_CPU_ACCELERATION_X86_AVX_128_FMA 1)
 -        set(GMX_X86_AVX_128_FMA 1)
 -        if (NOT ACCELERATION_QUIETLY)
 -          message(STATUS "Enabling 128-bit AVX Gromacs acceleration (with fused-multiply add)")
 -        endif()
 -
 -        # We don't have the full compiler version string yet (BUILD_C_COMPILER),
 -        # so we can't distinguish vanilla and Apple clang, but catering for AMD
 -        # hackintoshes is not worth the effort.
 -        if (APPLE AND ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
 -            message(WARNING "Due to a known compiler bug, Clang up to version 3.2 (and Apple Clang up to version 4.1) produces incorrect code with AVX_128_FMA acceleration. As we can not work around this bug on OS X, you will have to select a different compiler or CPU acceleration.")
 -        endif()
 -
 -        if (GMX_USE_CLANG_FMA_BUG_WORKAROUND)
 -            # we assume that we have an external assembler that supports AVX
 -            message(STATUS "Clang ${C_COMPILER_VERSION} detected, enabling FMA bug workaround")
 -            set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -no-integrated-as")
 -        endif()
 +##################################################
 +# Process SIMD instruction settings
 +##################################################
 +# This checks what flags to add in order to
 +# support the SIMD instructions we need, and sets
 +# correct defines for the SIMD instructions supported.
 +include(gmxTestSimd)
 +gmx_test_simd()
  
 -    else()
 -        # If we are not doing AVX_128, it must be AVX_256...
 -        set(GMX_CPU_ACCELERATION_X86_AVX_256 1)
 -        set(GMX_X86_AVX_256 1)
 -        if (NOT ACCELERATION_QUIETLY)
 -          message(STATUS "Enabling 256-bit AVX Gromacs acceleration")
 -        endif()
 -    endif()
 -
 -    # Unfortunately gcc-4.5.2 and gcc-4.6.0 has a bug where they use the wrong datatype for the formal
 -    # parameter of the mask for maskload/maskstore arguments. Check if this is present, since we can work around it.
 -    gmx_test_avx_gcc_maskload_bug(${ACCELERATION_C_FLAGS} GMX_X86_AVX_GCC_MASKLOAD_BUG)
 -
 -elseif(${GMX_CPU_ACCELERATION} STREQUAL "IBM_QPX")
 -    try_compile(TEST_QPX ${CMAKE_BINARY_DIR}
 -        "${CMAKE_SOURCE_DIR}/cmake/TestQPX.c")
 -
 -    if (TEST_QPX)
 -        message(WARNING "IBM QPX acceleration was selected. This will work, but SIMD-accelerated kernels are only available for the Verlet cut-off scheme. The plain C kernels that are used for the group cut-off scheme kernels will be slow, so please consider using the Verlet cut-off scheme.")
 -        set(GMX_CPU_ACCELERATION_IBM_QPX 1)
 -    else()
 -        message(FATAL_ERROR "Cannot compile the requested IBM QPX intrinsics. If you are compiling for BlueGene/Q with the XL compilers, use 'cmake .. -DCMAKE_TOOLCHAIN_FILE=Platform/BlueGeneQ-static-XL-C' to set up the tool chain.")
 -    endif()
 -elseif(${GMX_CPU_ACCELERATION} STREQUAL "SPARC64_HPC_ACE")
 -    set(GMX_CPU_ACCELERATION_SPARC64_HPC_ACE 1)
 -else(${GMX_CPU_ACCELERATION} STREQUAL "NONE")
 -    MESSAGE(FATAL_ERROR "Unrecognized option for accelerated kernels: ${GMX_CPU_ACCELERATION}. Pick one of None, SSE2, SSE4.1, AVX_128_FMA, AVX_256, IBM_QPX, Sparc64_HPC_ACE")
 -endif(${GMX_CPU_ACCELERATION} STREQUAL "NONE")
 -set(ACCELERATION_QUIETLY TRUE CACHE INTERNAL "")
  
  # Process QM/MM Settings
 -string(TOUPPER ${GMX_QMMM_PROGRAM} GMX_QMMM_PROGRAM)
  if(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
      set(GMX_QMMM_GAUSSIAN 1)
  elseif(${GMX_QMMM_PROGRAM} STREQUAL "MOPAC")
@@@ -652,21 -897,135 +664,21 @@@ elseif(${GMX_QMMM_PROGRAM} STREQUAL "OR
      set(GMX_QMMM_ORCA 1)
  elseif(${GMX_QMMM_PROGRAM} STREQUAL "NONE")
      # nothing to do
 -else(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
 -    MESSAGE(FATAL_ERROR "Invalid QM/MM program option: ${GMX_QMMM_PROGRAM}. Choose one of: Gaussian, Mopac, Gamess, Orca, None")
 -endif(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
 -
 -# Process FFT library settings
 -string(TOUPPER ${GMX_FFT_LIBRARY} GMX_FFT_LIBRARY)
 -set(PKG_FFT "")
 -set(PKG_FFT_LIBS "")
 -set(MKL_LIBRARIES_FORMAT_DESCRIPTION "Use full paths to library files, in the right order, and separated by semicolons.")
 -set(MKL_LIBRARIES "" CACHE STRING "List of libraries for linking to MKL. Only used with GMX_FFT_LIBRARY=mkl. ${MKL_LIBRARIES_FORMAT_DESCRIPTION}")
 -set(MKL_INCLUDE_DIR "" CACHE PATH "Path to mkl.h (non-inclusive). Only used with GMX_FFT_LIBRARY=mkl.")
 -mark_as_advanced(FORCE MKL_LIBRARIES)
 -mark_as_advanced(FORCE MKL_INCLUDE_DIR)
 -if(${GMX_FFT_LIBRARY} STREQUAL "FFTW3")
 -    if(GMX_DOUBLE)
 -        set(FFTW fftw)
 -    else()
 -        set(FFTW fftwf)
 -    endif()
 -
 -    if(GMX_BUILD_OWN_FFTW)
 -      add_subdirectory(src/contrib/fftw)
 -    else()
 -      find_package(FFTW COMPONENTS ${FFTW})
 -    endif()
 -
 -    string(TOUPPER "${FFTW}" FFTW)
 -    if(NOT ${FFTW}_FOUND)
 -      MESSAGE(FATAL_ERROR "Cannot find FFTW 3 (with correct precision - libfftw3f for single-precision GROMACS or libfftw3 for double-precision GROMACS). Either choose the right precision, choose another FFT(W) library (-DGMX_FFT_LIBRARY), enable the advanced option to let GROMACS build FFTW 3 for you (-DGMX_BUILD_OWN_FFTW=ON) , or use the really slow GROMACS built-in fftpack library (-DGMX_FFT_LIBRARY=fftpack).")
 -    endif()
 -
 -    set(PKG_FFT "${${FFTW}_PKG}")
 -    include_directories(${${FFTW}_INCLUDE_DIRS})
 -    set(FFT_LIBRARIES ${${FFTW}_LIBRARIES})
 -    set(GMX_FFT_FFTW3 1)
 -
 -    if ((${GMX_CPU_ACCELERATION} MATCHES "SSE" OR ${GMX_CPU_ACCELERATION} MATCHES "AVX") AND NOT ${FFTW}_HAVE_SIMD)
 -      message(WARNING "The fftw library found is compiled without SIMD support, which makes it slow. Consider recompiling it or contact your admin")
 -    endif()
 -
 -    if((${GMX_CPU_ACCELERATION} MATCHES "SSE" OR ${GMX_CPU_ACCELERATION} MATCHES "AVX") AND ${FFTW}_HAVE_AVX)
 -        # If we're not doing CPU acceleration, we don't care about FFTW performance on x86 either
 -        message(WARNING "The FFTW library was compiled with --enable-avx to enable AVX SIMD instructions. That might sound like a good idea for your processor, but for FFTW versions up to 3.3.3, these are slower than the SSE/SSE2 SIMD instructions for the way GROMACS uses FFTs. Limitations in the way FFTW allows GROMACS to measure performance make it awkward for either GROMACS or FFTW to make the decision for you based on runtime performance. You should compile a different FFTW library with --enable-sse or --enable-sse2. If you have a more recent FFTW, you may like to compare the performance of GROMACS with FFTW libraries compiled with and without --enable-avx. However, the GROMACS developers do not really expect the FFTW AVX optimization to help, because the performance is limited by memory access, not computation.")
 -    endif()
 -
 -    if(NOT "${GMX_FFT_LIBRARY}" STREQUAL "${GMX_FFT_LIBRARY_PREVIOUS_VALUE}")
 -        MESSAGE(STATUS "Using external FFT library - FFTW3")
 -    endif()
 -elseif(${GMX_FFT_LIBRARY} STREQUAL "MKL")
 -    # Intel 11 and up makes life somewhat easy if you just want to use
 -    # all their stuff. It's not easy if you only want some of their
 -    # stuff...
 -    if (CMAKE_C_COMPILER_ID MATCHES "Intel" AND C_COMPILER_VERSION VERSION_GREATER "11")
 -        # The next line takes care of everything for MKL
 -        if (WIN32)
 -            set(FFT_LINKER_FLAGS "/Qmkl=sequential")
 -        else()
 -            set(FFT_LINKER_FLAGS "-mkl=sequential")
 -        endif()
 -        # Some versions of icc require this in order that mkl.h can be
 -        # found at compile time.
 -        set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} ${FFT_LINKER_FLAGS}")
 -
 -        set(MKL_ERROR_MESSAGE "Make sure you have configured your compiler so that ${FFT_LINKER_FLAGS} will work.")
 -    else()
 -        # The user will have to provide the set of magic libraries in
 -        # MKL_LIBRARIES, which we cache (non-advanced), so that they
 -        # don't have to keep specifying it, and can easily see that
 -        # CMake is still using that information.
 -        set(MKL_LIBRARIES "${MKL_LIBRARIES}" CACHE STRING "User-specified libraries for linking to MKL")
 -        mark_as_advanced(CLEAR MKL_LIBRARIES)
 -        include_directories(${MKL_INCLUDE_DIR})
 -        mark_as_advanced(CLEAR MKL_INCLUDE_DIR)
 -        set(FFT_LIBRARIES "${MKL_LIBRARIES}")
 -        set(MKL_ERROR_MESSAGE "The include path to mkl.h in MKL_INCLUDE_DIR, and the link libraries in MKL_LIBRARIES=${MKL_LIBRARIES} need to match what the MKL documentation says you need for your system. ${MKL_LIBRARIES_FORMAT_DESCRIPTION}")
 -        # Convert the semi-colon separated list to a list of
 -        # command-line linker arguments so that code using our
 -        # pkgconfig setup can use it.
 -        string(REGEX REPLACE ";" " " PKG_FFT_LIBS "${MKL_LIBRARIES}")
 -    endif()
 -
 -    # Check MKL works. If we were in a non-global scope, we wouldn't
 -    # have to play nicely.
 -    set(old_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
 -    set(CMAKE_REQUIRED_FLAGS "${FFT_LINKER_FLAGS}")
 -    set(old_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}")
 -    set(CMAKE_REQUIRED_LIBRARIES "${FFT_LIBRARIES}")
 -
 -    check_function_exists(DftiCreateDescriptor TEST_MKL)
 -
 -    set(CMAKE_REQUIRED_FLAGS "${old_CMAKE_REQUIRED_FLAGS}")
 -    set(CMAKE_REQUIRED_LIBRARIES "${old_CMAKE_REQUIRED_LIBRARIES}")
 -
 -    if(NOT TEST_MKL)
 -        # Hack to help the user vary MKL settings until they work.
 -        # TODO Make this logic more useful.
 -        unset(TEST_MKL CACHE)
 -        message(FATAL_ERROR "Linking with MKL was requested, but was not successful. ${MKL_ERROR_MESSAGE}")
 -    endif()
 +else()
 +    gmx_invalid_option_value(GMX_QMMM_PROGRAM)
 +endif()
  
 -    # Set variables to signal that we have MKL available and should use it for FFTs.
 -    set(GMX_FFT_MKL 1)
 -    set(HAVE_LIBMKL 1)
  
 -    if(NOT "${GMX_FFT_LIBRARY}" STREQUAL "${GMX_FFT_LIBRARY_PREVIOUS_VALUE}")
 -        MESSAGE(STATUS "Using external FFT library - Intel MKL")
 -    endif()
 +##################################################
 +# Process FFT library settings
 +##################################################
 +include(gmxManageFFTLibraries)
  
 -#elseif(${GMX_FFT_LIBRARY} STREQUAL "ACML")
 -#    MESSAGE(STATUS "Using external FFT library - AMD core math library")
 -#    set(GMX_FFT_ACML 1)
 -elseif(${GMX_FFT_LIBRARY} STREQUAL "FFTPACK")
 -    set(GMX_FFT_FFTPACK 1)
 -    if(NOT "${GMX_FFT_LIBRARY}" STREQUAL "${GMX_FFT_LIBRARY_PREVIOUS_VALUE}")
 -        MESSAGE(STATUS "Using internal FFT library - fftpack")
 -    endif()
 -else(${GMX_FFT_LIBRARY} STREQUAL "FFTW3")
 -    MESSAGE(FATAL_ERROR "Invalid FFT library setting: ${GMX_FFT_LIBRARY}. Choose one of: fftw3, mkl, fftpack")
 -endif(${GMX_FFT_LIBRARY} STREQUAL "FFTW3")
 -set(GMX_FFT_LIBRARY_PREVIOUS_VALUE ${GMX_FFT_LIBRARY} CACHE INTERNAL "Previous value for GMX_FFT_LIBRARY, so that we can detect when it changes.")
 -
 -# enable threaded fftw3 if we've found it 
 -if(FFTW3_THREADS OR FFTW3F_THREADS)
 -    add_definitions(-DFFT5D_FFTW_THREADS)
 -endif()
  
  include(gmxManageLinearAlgebraLibraries)
  
 -set(GMX_USE_PLUGINS OFF CACHE INTERNAL "Whether GROMACS will really try to compile support for VMD plugins")
 +# Whether GROMACS will really try to compile support for VMD plugins
 +set(GMX_USE_PLUGINS OFF)
  
  if(GMX_LOAD_PLUGINS)
    if(CYGWIN OR NOT WIN32)
    else()
      set(PKG_DL_LIBS)
    endif()
 -endif(GMX_LOAD_PLUGINS)
 +endif()
  set(VMD_QUIETLY TRUE CACHE INTERNAL "")
  
 -if(EXISTS "${CMAKE_SOURCE_DIR}/admin/.isreposource")
 -    if(NOT CMAKE_CROSSCOMPILING)
 -        option(GMX_BUILD_MANPAGES "Build man pages" ON)
 -    else()
 -        message(STATUS "Building the man pages is not available when "
 -            "cross-compiling the developer version from git")
 -    endif()
 -else()
 -    #make sure source package contains all man pages
 -    if(NOT EXISTS "${CMAKE_SOURCE_DIR}/man/man1/ngmx.1")
 -        message(FATAL_ERROR "Man pages are missing from source package.")
 -    endif()
 +# Link real-time library for POSIX timers. The check for clock_gettime
 +# confirms the linkability of rt.
 +if(HAVE_TIME_H AND HAVE_UNISTD_H AND HAVE_CLOCK_GETTIME)
 +    list(APPEND GMX_EXTRA_LIBRARIES rt)
  endif()
 -mark_as_advanced(GMX_BUILD_MANPAGES)
  
  # Math and thread libraries must often come after all others when linking...
  if(HAVE_LIBM)
 -    list(APPEND       GMX_EXTRA_LIBRARIES m)
 -endif(HAVE_LIBM)
 -if (${CMAKE_SYSTEM_NAME} MATCHES "BlueGene")
 -    check_library_exists(mass_simd atan2f4 "" HAVE_MASS_SIMD)
 -    if(HAVE_MASS_SIMD)
 -        list(APPEND GMX_EXTRA_LIBRARIES mass_simd)
 -    else()
 -        message(FATAL_ERROR "Could not link to the SIMD version of the IBM MASS library. Please adjust your CMAKE_PREFIX_PATH to contain it")
 -    endif()
 +    list(APPEND GMX_EXTRA_LIBRARIES m)
  endif()
  
 -
  option(GMX_NACL "Configure for Native Client builds" OFF)
  if (GMX_NACL)
    list(APPEND GMX_EXTRA_LIBRARIES nosys)
@@@ -717,37 -1094,29 +729,37 @@@ endif(
  mark_as_advanced(GMX_NACL)
  
  if(GMX_FAHCORE)
 -  set(COREWRAP_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../corewrap" CACHE STRING 
 +  set(COREWRAP_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../corewrap" CACHE STRING
        "Path to swindirect.h")
    include_directories(${COREWRAP_INCLUDE_DIR})
 -  set_property(CACHE GMX_COOL_QUOTES PROPERTY VALUE OFF)
 -endif(GMX_FAHCORE)
 +endif()
 +
 +option(GMX_BUILD_HELP "Build man pages, HTML help, and completions automatically (requires that compiled binaries can be executed on the build host)" OFF)
 +mark_as_advanced(GMX_BUILD_HELP)
 +if (GMX_BUILD_HELP AND SOURCE_IS_SOURCE_DISTRIBUTION AND
 +    "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
 +
 +    message(FATAL_ERROR
 +        "Rebuilding HTML and man pages is not supported for in-source "
 +        "builds from a source distribution. "
 +        "Set GMX_BUILD_HELP=OFF or do an out-of-source build to proceed.")
 +endif()
  
  # # # # # # # # # # NO MORE TESTS AFTER THIS LINE! # # # # # # # # # # #
  # these are set after everything else
  if (NOT GMX_SKIP_DEFAULT_CFLAGS)
 -    set(CMAKE_C_FLAGS "${ACCELERATION_C_FLAGS} ${MPI_COMPILE_FLAGS} ${EXTRA_C_FLAGS} ${CMAKE_C_FLAGS}")
 -    set(CMAKE_CXX_FLAGS "${ACCELERATION_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${CMAKE_CXX_FLAGS}")
 +    set(CMAKE_C_FLAGS "${SIMD_C_FLAGS} ${MPI_COMPILE_FLAGS} ${EXTRA_C_FLAGS} ${CMAKE_C_FLAGS}")
 +    set(CMAKE_CXX_FLAGS "${SIMD_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${EXTRA_CXX_FLAGS} ${CMAKE_CXX_FLAGS}")
      set(CMAKE_EXE_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
 -    set(CMAKE_SHARED_LINKER_FLAGS "${MPI_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
 +    set(CMAKE_SHARED_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
  else()
      message("Recommended flags which are not added because GMX_SKIP_DEFAULT_CFLAGS=yes:")
 -    message("CMAKE_C_FLAGS: ${ACCELERATION_C_FLAGS} ${MPI_COMPILE_FLAGS} ${EXTRA_C_FLAGS} ${GMXC_CFLAGS}")
 +    message("CMAKE_C_FLAGS: ${SIMD_C_FLAGS} ${MPI_COMPILE_FLAGS} ${EXTRA_C_FLAGS} ${GMXC_CFLAGS}")
      message("CMAKE_C_FLAGS_RELEASE: ${GMXC_CFLAGS_RELEASE}")
      message("CMAKE_C_FLAGS_DEBUG: ${GMXC_CFLAGS_DEBUG}")
 -    if(CMAKE_CXX_COMPILER_LOADED)
 -        message("CMAKE_CXX_FLAGS: ${ACCELERATION_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${GMXC_CXXFLAGS}")
 -        message("CMAKE_CXX_FLAGS_RELEASE: ${GMXC_CXXFLAGS_RELEASE}")
 -        message("CMAKE_CXX_FLAGS_DEBUG: ${GMXC_CXXFLAGS_DEBUG}")
 -    endif()
 +    message("CMAKE_CXX_FLAGS: ${SIMD_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${EXTRA_CXX_FLAGS} ${GMXC_CXXFLAGS}")
 +    message("CMAKE_CXX_FLAGS_RELEASE: ${GMXC_CXXFLAGS_RELEASE}")
 +    message("CMAKE_CXX_FLAGS_DEBUG: ${GMXC_CXXFLAGS_DEBUG}")
      message("CMAKE_EXE_LINKER_FLAGS: ${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS}")
      message("CMAKE_SHARED_LINKER_FLAGS: ${MPI_LINKER_FLAGS}")
  endif()
  if(NOT GMX_OPENMP)
      #Unset all OpenMP flags in case OpenMP was disabled either by the user
      #or because it was only partially detected (e.g. only for C but not C++ compiler)
 -    unset(OpenMP_C_FLAGS CACHE) 
 +    unset(OpenMP_C_FLAGS CACHE)
      unset(OpenMP_CXX_FLAGS CACHE)
 -    unset(OpenMP_LINKER_FLAGS CACHE)
 -    unset(OpenMP_SHARED_LINKER_FLAGS)
 +else()
 +    set(GMX_EXE_LINKER_FLAGS ${GMX_EXE_LINKER_FLAGS} ${OpenMP_LINKER_FLAGS})
 +    set(GMX_SHARED_LINKER_FLAGS ${GMX_SHARED_LINKER_FLAGS} ${OpenMP_SHARED_LINKER_FLAGS})
  endif()
  set(PKG_CFLAGS "${PKG_CFLAGS} ${OpenMP_C_FLAGS}")
  
 -######################################
 -# Output compiler and CFLAGS used
 -######################################
 -get_compiler_info(C BUILD_C_COMPILER BUILD_CFLAGS)
 -if (CMAKE_CXX_COMPILER_LOADED)
 -    get_compiler_info(CXX BUILD_CXX_COMPILER BUILD_CXXFLAGS)
 -endif ()
 -if(GMX_GPU)
 -    get_cuda_compiler_info(CUDA_NVCC_COMPILER_INFO CUDA_NVCC_COMPILER_FLAGS)
 -endif(GMX_GPU)
 -
 -
  ########################################################################
 -# Specify install locations and which subdirectories to process        #
 +# Specify install locations
  ########################################################################
 -if (GMX_USE_RELATIVE_INSTALL_PATH)
 -    set(GMX_INSTALL_PREFIX "" CACHE STRING "Prefix gets appended to CMAKE_INSTALL_PREFIX. For cpack it sets the root folder of the archive.")
 -    mark_as_advanced(GMX_INSTALL_PREFIX)
 -else()
 -    set(GMX_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/")
 -endif()
 +set(GMX_LIB_INSTALL_DIR lib CACHE STRING
 +    "Library installation directory (default: lib)")
 +set(GMX_DATA_INSTALL_DIR gromacs CACHE STRING
 +    "Data installation directory under share/ (default: gromacs)")
 +mark_as_advanced(GMX_LIB_INSTALL_DIR GMX_DATA_INSTALL_DIR)
  
 -if ( NOT DEFINED GMXLIB )
 -    set(GMXLIB lib)
 -endif()
 -set(LIB_INSTALL_DIR "${GMX_INSTALL_PREFIX}${GMXLIB}")
 +set(LIB_INSTALL_DIR  ${GMX_INSTALL_PREFIX}${GMX_LIB_INSTALL_DIR})
  set(BIN_INSTALL_DIR  ${GMX_INSTALL_PREFIX}bin)
 -set(DATA_INSTALL_DIR ${GMX_INSTALL_PREFIX}share/gromacs)
 +set(DATA_INSTALL_DIR ${GMX_INSTALL_PREFIX}share/${GMX_DATA_INSTALL_DIR})
  set(MAN_INSTALL_DIR  ${GMX_INSTALL_PREFIX}share/man)
  set(INCL_INSTALL_DIR ${GMX_INSTALL_PREFIX}include)
  
 -set(GMXLIBDIR        ${DATA_INSTALL_DIR}/top)
 +set(GMXLIB_SEARCH_DIR share/${GMX_DATA_INSTALL_DIR}/top)
 +set(GMXLIB_FALLBACK   ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/top)
  
 -########################################################################
 -# Set up binary and library suffixing
 -########################################################################
 -set(GMX_BINARY_SUFFIX "" CACHE STRING "Suffix for GROMACS binaries (default: _d for double, _mpi for MPI, _mpi_d for MPI and double).")
 -set(GMX_LIBS_SUFFIX ""
 -  CACHE STRING "Suffix for GROMACS libs (default: _d for double, _mpi for MPI, _mpi_d for MPI and double).")
 -if (GMX_DEFAULT_SUFFIX)
 -  set(GMX_BINARY_SUFFIX "")
 -  set(GMX_LIBS_SUFFIX "")
 -  if (GMX_LIB_MPI)
 -    set(GMX_BINARY_SUFFIX "_mpi")
 -    set(GMX_LIBS_SUFFIX "_mpi")
 -  endif()
 -  if (GMX_DOUBLE)
 -    set (GMX_BINARY_SUFFIX "${GMX_BINARY_SUFFIX}_d")
 -    set (GMX_LIBS_SUFFIX "${GMX_LIBS_SUFFIX}_d")
 -  endif(GMX_DOUBLE)
 -  mark_as_advanced(FORCE GMX_BINARY_SUFFIX GMX_LIBS_SUFFIX)
 -  if (NOT SUFFIX_QUIETLY)
 -    message(STATUS "Using default binary suffix: \"${GMX_BINARY_SUFFIX}\"")
 -    message(STATUS "Using default library suffix: \"${GMX_LIBS_SUFFIX}\"")
 -  endif (NOT SUFFIX_QUIETLY)
 -else(GMX_DEFAULT_SUFFIX)
 -  mark_as_advanced(CLEAR GMX_BINARY_SUFFIX GMX_LIBS_SUFFIX)
 -  if (NOT SUFFIX_QUIETLY)
 -    message(STATUS "Using manually set binary suffix: \"${GMX_BINARY_SUFFIX}\"")
 -    message(STATUS "Using manually set library suffix: \"${GMX_LIBS_SUFFIX}\"")
 -  endif (NOT SUFFIX_QUIETLY)
 -endif(GMX_DEFAULT_SUFFIX)
 -set(SUFFIX_QUIETLY TRUE CACHE INTERNAL "")
 +# Binary and library suffix options
 +include(gmxManageSuffixes)
  
  ##################################################################
 -# Shared library settings - Darwin uses INSTALL_NAME_DIR instead!
 +# Shared library settings
  ##################################################################
  if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 -    set(CMAKE_SKIP_BUILD_RPATH  FALSE)
 -    set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
 -    set(CMAKE_INSTALL_RPATH "\\\$ORIGIN/../${GMXLIB}")
 +    if(GMX_LIB_INSTALL_DIR STREQUAL "lib")
 +        set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
 +    endif()
 +    set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${GMX_LIB_INSTALL_DIR}")
      set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
 +else()
 +    if(CMAKE_SYSTEM_VERSION VERSION_GREATER 8.0) #rpath supported for >10.4
 +        set(CMAKE_INSTALL_NAME_DIR "@rpath")
 +        set(GMX_EXE_LINKER_FLAGS ${GMX_EXE_LINKER_FLAGS} "-Wl,-rpath,@executable_path/../${GMX_LIB_INSTALL_DIR}")
 +    else()
 +        set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}")
 +    endif()
  endif()
  
  #COPYING file: Only necessary for binary distributions.
  #Simpler to always install.
  install(FILES COPYING DESTINATION ${DATA_INSTALL_DIR} COMPONENT data)
  
 -add_subdirectory(share)
 -add_subdirectory(include)
 +if(GMX_EXTERNAL_BOOST)
 +    include_directories(${Boost_INCLUDE_DIRS})
 +    set(PKG_CFLAGS "${PKG_CFLAGS} -I${Boost_INCLUDE_DIRS}")
 +else()
 +    include_directories(${CMAKE_SOURCE_DIR}/src/external/boost)
 +    # typeid not supported for minimal internal version
 +    # (would add significant amount of code)
 +    add_definitions(-DBOOST_NO_TYPEID)
 +    # TODO: Propagate the above settings to the installed CMakeFiles.txt template
 +    # (from share/template/)
 +    set(PKG_CFLAGS "${PKG_CFLAGS} -DBOOST_NO_TYPEID -I${CMAKE_INSTALL_PREFIX}/${INCL_INSTALL_DIR}/gromacs/external/boost")
 +    if (NOT GMX_BUILD_MDRUN_ONLY)
 +        install(DIRECTORY ${CMAKE_SOURCE_DIR}/src/external/boost/boost
 +                DESTINATION ${INCL_INSTALL_DIR}/gromacs/external/boost
 +                COMPONENT development)
 +    endif()
 +endif()
 +
 +if(GMX_USE_TNG)
 +    find_package(ZLIB QUIET)
 +    include(gmxTestZLib)
 +    gmx_test_zlib(HAVE_ZLIB)
 +    set(TNG_BUILD_WITH_ZLIB ${HAVE_ZLIB} CACHE BOOL  "Build TNG with zlib compression")
 +    set(TNG_BUILD_FORTRAN OFF CACHE BOOL "Build Fortran compatible TNG library and examples for testing")
 +    set(TNG_BUILD_EXAMPLES OFF CACHE BOOL "Build examples showing usage of the TNG API")
 +    set(TNG_BUILD_COMPRESSION_TESTS OFF CACHE BOOL "Build tests of the TNG compression library")
 +    set(TNG_BUILD_DOCUMENTATION OFF CACHE BOOL "Use Doxygen to create the HTML based TNG API documentation")
 +    set(TNG_BUILD_TEST OFF CACHE BOOL "Build TNG testing binary.")
 +    add_subdirectory(${CMAKE_SOURCE_DIR}/src/external/tng_io)
 +    set(GMX_TNG_LIBRARIES tng_io)
 +endif()
 +mark_as_advanced(TNG_BUILD_FORTRAN)
 +mark_as_advanced(TNG_BUILD_EXAMPLES)
 +mark_as_advanced(TNG_BUILD_COMPRESSION_TESTS)
 +mark_as_advanced(TNG_BUILD_DOCUMENTATION)
 +mark_as_advanced(TNG_BUILD_TEST)
 +mark_as_advanced(TNG_EXAMPLE_FILES_DIR)
 +
 +if (GMX_BUILD_FOR_COVERAGE)
 +    # Code heavy with asserts makes conditional coverage close to useless metric,
 +    # as by design most of the false branches are impossible to trigger in
 +    # correctly functioning code.  And the benefit of testing those that could
 +    # be triggered by using an API against its specification isn't usually
 +    # worth the effort.
 +    add_definitions(-DNDEBUG -DBOOST_DISABLE_ASSERTS -DGMX_DISABLE_ASSERTS)
 +endif()
 +
 +if (BUILD_TESTING)
 +    # "tests" target builds all the separate test binaries.
 +    add_custom_target(tests)
 +    # "run-ctest" is an internal target that actually runs the tests.
 +    # This is necessary to be able to add separate targets that execute as part
 +    # of 'make check', but are ensured to be executed after the actual tests.
 +    add_custom_target(run-ctest
 +                      COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
 +                      COMMENT "Running all tests"
 +                      VERBATIM)
 +    add_dependencies(run-ctest tests)
 +    # "check" target builds and runs all tests.
 +    add_custom_target(check DEPENDS run-ctest)
 +endif()
 +
 +if (NOT GMX_BUILD_MDRUN_ONLY)
 +    add_subdirectory(doxygen)
 +    add_subdirectory(share)
 +    add_subdirectory(scripts)
 +endif()
  add_subdirectory(src)
 -add_subdirectory(scripts)
 +
 +if (BUILD_TESTING)
 +    add_subdirectory(tests)
 +endif()
  
  # Issue a warning if NVIDIA GPUs were detected, but CUDA was not found.
  # Don't bother the user after the first configure pass.
  if ((CUDA_NOTFOUND_AUTO AND GMX_DETECT_GPU_AVAILABLE) AND NOT GMX_GPU_DETECTION_DONE)
      message(WARNING "${CUDA_NOTFOUND_MESSAGE}")
 -    unset(CUDA_NOTFOUND_AUTO)
 -    unset(CUDA_NOTFOUND_MESSAGE)
  endif()
  set(GMX_GPU_DETECTION_DONE TRUE CACHE INTERNAL "Whether GPU detection has already been done")
  
                     IMMEDIATE @ONLY)
  ###########################
  ADD_CUSTOM_TARGET(uninstall
 -                  "${CMAKE_COMMAND}" -P 
 +                  "${CMAKE_COMMAND}" -P
                    "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake")
  ###########################
  
  ########################################################################
 -# Tests                                                                #
 +# Manual                                                               #
  ########################################################################
  
 -include(CTest)
 -mark_as_advanced(BUILD_TESTING)
 -#gmxtests target builds all binaries required for running gmxtest
 -add_custom_target(gmxtests DEPENDS grompp mdrun pdb2gmx gmxcheck editconf)
 -IF(BUILD_TESTING)
 -    enable_testing()
 -    add_subdirectory(tests)
 -    if(REGRESSIONTEST_PATH)
 -        #check target builds all to run tests and the runs tests
 -        add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure)
 -        add_dependencies(check gmxtests)
 -    else()
 -        add_custom_target(check COMMAND ${CMAKE_COMMAND} -E echo "WARNING: No tests are run. Running the tests requires either of the cmake variables REGRESSIONTEST_PATH or REGRESSIONTEST_DOWNLOAD to be set.")
 -    endif()
 -ENDIF()
 -
 +option(GMX_BUILD_MANUAL "Whether to try to configure to build the PDF manual" OFF)
 +mark_as_advanced(GMX_BUILD_MANUAL)
 +if(GMX_BUILD_MANUAL)
 +    # Make sure we only do detection of manual-building dependencies
 +    # when the user opted in for that.
 +    add_subdirectory(manual)
 +endif()
index 501597e9acb7addff70ca18cbb79a2a8eb22395d,0000000000000000000000000000000000000000..c9faa784de876a09870107c1decc63a88341790f
mode 100644,000000..100644
--- /dev/null
@@@ -1,657 -1,0 +1,660 @@@
-     for (i = 0; (i < ndim); i++)
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include <time.h>
 +
 +#ifdef HAVE_SYS_TIME_H
 +#include <sys/time.h>
 +#endif
 +
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "mshift.h"
 +#include "xvgr.h"
 +#include "rmpbc.h"
 +#include "txtdump.h"
 +#include "gromacs/fileio/matio.h"
 +#include "eigio.h"
 +#include "physics.h"
 +#include "gmx_ana.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +#include "gromacs/linearalgebra/eigensolver.h"
 +#include "gromacs/math/do_fit.h"
 +#include "gromacs/legacyheaders/gmx_fatal.h"
 +
 +int gmx_covar(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] calculates and diagonalizes the (mass-weighted)",
 +        "covariance matrix.",
 +        "All structures are fitted to the structure in the structure file.",
 +        "When this is not a run input file periodicity will not be taken into",
 +        "account. When the fit and analysis groups are identical and the analysis",
 +        "is non mass-weighted, the fit will also be non mass-weighted.",
 +        "[PAR]",
 +        "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
 +        "When the same atoms are used for the fit and the covariance analysis,",
 +        "the reference structure for the fit is written first with t=-1.",
 +        "The average (or reference when [TT]-ref[tt] is used) structure is",
 +        "written with t=0, the eigenvectors",
 +        "are written as frames with the eigenvector number as timestamp.",
 +        "[PAR]",
 +        "The eigenvectors can be analyzed with [gmx-anaeig].",
 +        "[PAR]",
 +        "Option [TT]-ascii[tt] writes the whole covariance matrix to",
 +        "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
 +        "[PAR]",
 +        "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
 +        "[PAR]",
 +        "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
 +        "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
 +        "written.",
 +        "[PAR]",
 +        "Note that the diagonalization of a matrix requires memory and time",
 +        "that will increase at least as fast as than the square of the number",
 +        "of atoms involved. It is easy to run out of memory, in which",
 +        "case this tool will probably exit with a 'Segmentation fault'. You",
 +        "should consider carefully whether a reduced set of atoms will meet",
 +        "your needs for lower costs."
 +    };
 +    static gmx_bool bFit = TRUE, bRef = FALSE, bM = FALSE, bPBC = TRUE;
 +    static int      end  = -1;
 +    t_pargs         pa[] = {
 +        { "-fit",  FALSE, etBOOL, {&bFit},
 +          "Fit to a reference structure"},
 +        { "-ref",  FALSE, etBOOL, {&bRef},
 +          "Use the deviation from the conformation in the structure file instead of from the average" },
 +        { "-mwa",  FALSE, etBOOL, {&bM},
 +          "Mass-weighted covariance analysis"},
 +        { "-last",  FALSE, etINT, {&end},
 +          "Last eigenvector to write away (-1 is till the last)" },
 +        { "-pbc",  FALSE,  etBOOL, {&bPBC},
 +          "Apply corrections for periodic boundary conditions" }
 +    };
 +    FILE           *out;
 +    t_trxstatus    *status;
 +    t_trxstatus    *trjout;
 +    t_topology      top;
 +    int             ePBC;
 +    t_atoms        *atoms;
 +    rvec           *x, *xread, *xref, *xav, *xproj;
 +    matrix          box, zerobox;
 +    real           *sqrtm, *mat, *eigenvalues, sum, trace, inv_nframes;
 +    real            t, tstart, tend, **mat2;
 +    real            xj, *w_rls = NULL;
 +    real            min, max, *axis;
 +    int             ntopatoms, step;
 +    int             natoms, nat, count, nframes0, nframes, nlevels;
 +    gmx_int64_t     ndim, i, j, k, l;
 +    int             WriteXref;
 +    const char     *fitfile, *trxfile, *ndxfile;
 +    const char     *eigvalfile, *eigvecfile, *averfile, *logfile;
 +    const char     *asciifile, *xpmfile, *xpmafile;
 +    char            str[STRLEN], *fitname, *ananame, *pcwd;
 +    int             d, dj, nfit;
 +    atom_id        *index, *ifit;
 +    gmx_bool        bDiffMass1, bDiffMass2;
 +    time_t          now;
 +    char            timebuf[STRLEN];
 +    t_rgb           rlo, rmi, rhi;
 +    real           *eigenvectors;
 +    output_env_t    oenv;
 +    gmx_rmpbc_t     gpbc = NULL;
 +
 +    t_filenm        fnm[] = {
 +        { efTRX, "-f",  NULL, ffREAD },
 +        { efTPS, NULL,  NULL, ffREAD },
 +        { efNDX, NULL,  NULL, ffOPTRD },
 +        { efXVG, NULL,  "eigenval", ffWRITE },
 +        { efTRN, "-v",  "eigenvec", ffWRITE },
 +        { efSTO, "-av", "average.pdb", ffWRITE },
 +        { efLOG, NULL,  "covar", ffWRITE },
 +        { efDAT, "-ascii", "covar", ffOPTWR },
 +        { efXPM, "-xpm", "covar", ffOPTWR },
 +        { efXPM, "-xpma", "covara", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    clear_mat(zerobox);
 +
 +    fitfile    = ftp2fn(efTPS, NFILE, fnm);
 +    trxfile    = ftp2fn(efTRX, NFILE, fnm);
 +    ndxfile    = ftp2fn_null(efNDX, NFILE, fnm);
 +    eigvalfile = ftp2fn(efXVG, NFILE, fnm);
 +    eigvecfile = ftp2fn(efTRN, NFILE, fnm);
 +    averfile   = ftp2fn(efSTO, NFILE, fnm);
 +    logfile    = ftp2fn(efLOG, NFILE, fnm);
 +    asciifile  = opt2fn_null("-ascii", NFILE, fnm);
 +    xpmfile    = opt2fn_null("-xpm", NFILE, fnm);
 +    xpmafile   = opt2fn_null("-xpma", NFILE, fnm);
 +
 +    read_tps_conf(fitfile, str, &top, &ePBC, &xref, NULL, box, TRUE);
 +    atoms = &top.atoms;
 +
 +    if (bFit)
 +    {
 +        printf("\nChoose a group for the least squares fit\n");
 +        get_index(atoms, ndxfile, 1, &nfit, &ifit, &fitname);
 +        if (nfit < 3)
 +        {
 +            gmx_fatal(FARGS, "Need >= 3 points to fit!\n");
 +        }
 +    }
 +    else
 +    {
 +        nfit = 0;
 +    }
 +    printf("\nChoose a group for the covariance analysis\n");
 +    get_index(atoms, ndxfile, 1, &natoms, &index, &ananame);
 +
 +    bDiffMass1 = FALSE;
 +    if (bFit)
 +    {
 +        snew(w_rls, atoms->nr);
 +        for (i = 0; (i < nfit); i++)
 +        {
 +            w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
 +            if (i)
 +            {
 +                bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]] != w_rls[ifit[i-1]]);
 +            }
 +        }
 +    }
 +    bDiffMass2 = FALSE;
 +    snew(sqrtm, natoms);
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        if (bM)
 +        {
 +            sqrtm[i] = sqrt(atoms->atom[index[i]].m);
 +            if (i)
 +            {
 +                bDiffMass2 = bDiffMass2 || (sqrtm[i] != sqrtm[i-1]);
 +            }
 +        }
 +        else
 +        {
 +            sqrtm[i] = 1.0;
 +        }
 +    }
 +
 +    if (bFit && bDiffMass1 && !bDiffMass2)
 +    {
 +        bDiffMass1 = natoms != nfit;
 +        i          = 0;
 +        for (i = 0; (i < natoms) && !bDiffMass1; i++)
 +        {
 +            bDiffMass1 = index[i] != ifit[i];
 +        }
 +        if (!bDiffMass1)
 +        {
 +            fprintf(stderr, "\n"
 +                    "Note: the fit and analysis group are identical,\n"
 +                    "      while the fit is mass weighted and the analysis is not.\n"
 +                    "      Making the fit non mass weighted.\n\n");
 +            for (i = 0; (i < nfit); i++)
 +            {
 +                w_rls[ifit[i]] = 1.0;
 +            }
 +        }
 +    }
 +
 +    /* Prepare reference frame */
 +    if (bPBC)
 +    {
 +        gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
 +        gmx_rmpbc(gpbc, atoms->nr, box, xref);
 +    }
 +    if (bFit)
 +    {
 +        reset_x(nfit, ifit, atoms->nr, NULL, xref, w_rls);
 +    }
 +
 +    snew(x, natoms);
 +    snew(xav, natoms);
 +    ndim = natoms*DIM;
 +    if (sqrt(GMX_INT64_MAX) < ndim)
 +    {
 +        gmx_fatal(FARGS, "Number of degrees of freedoms to large for matrix.\n");
 +    }
 +    snew(mat, ndim*ndim);
 +
 +    fprintf(stderr, "Calculating the average structure ...\n");
 +    nframes0 = 0;
 +    nat      = read_first_x(oenv, &status, trxfile, &t, &xread, box);
 +    if (nat != atoms->nr)
 +    {
 +        fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, nat);
 +    }
 +    do
 +    {
 +        nframes0++;
 +        /* calculate x: a fitted struture of the selected atoms */
 +        if (bPBC)
 +        {
 +            gmx_rmpbc(gpbc, nat, box, xread);
 +        }
 +        if (bFit)
 +        {
 +            reset_x(nfit, ifit, nat, NULL, xread, w_rls);
 +            do_fit(nat, w_rls, xref, xread);
 +        }
 +        for (i = 0; i < natoms; i++)
 +        {
 +            rvec_inc(xav[i], xread[index[i]]);
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, xread, box));
 +    close_trj(status);
 +
 +    inv_nframes = 1.0/nframes0;
 +    for (i = 0; i < natoms; i++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            xav[i][d]         *= inv_nframes;
 +            xread[index[i]][d] = xav[i][d];
 +        }
 +    }
 +    write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure",
 +                           atoms, xread, NULL, epbcNONE, zerobox, natoms, index);
 +    sfree(xread);
 +
 +    fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", (int)ndim, (int)ndim);
 +    nframes = 0;
 +    nat     = read_first_x(oenv, &status, trxfile, &t, &xread, box);
 +    tstart  = t;
 +    do
 +    {
 +        nframes++;
 +        tend = t;
 +        /* calculate x: a (fitted) structure of the selected atoms */
 +        if (bPBC)
 +        {
 +            gmx_rmpbc(gpbc, nat, box, xread);
 +        }
 +        if (bFit)
 +        {
 +            reset_x(nfit, ifit, nat, NULL, xread, w_rls);
 +            do_fit(nat, w_rls, xref, xread);
 +        }
 +        if (bRef)
 +        {
 +            for (i = 0; i < natoms; i++)
 +            {
 +                rvec_sub(xread[index[i]], xref[index[i]], x[i]);
 +            }
 +        }
 +        else
 +        {
 +            for (i = 0; i < natoms; i++)
 +            {
 +                rvec_sub(xread[index[i]], xav[i], x[i]);
 +            }
 +        }
 +
 +        for (j = 0; j < natoms; j++)
 +        {
 +            for (dj = 0; dj < DIM; dj++)
 +            {
 +                k  = ndim*(DIM*j+dj);
 +                xj = x[j][dj];
 +                for (i = j; i < natoms; i++)
 +                {
 +                    l = k+DIM*i;
 +                    for (d = 0; d < DIM; d++)
 +                    {
 +                        mat[l+d] += x[i][d]*xj;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, xread, box) &&
 +           (bRef || nframes < nframes0));
 +    close_trj(status);
 +    gmx_rmpbc_done(gpbc);
 +
 +    fprintf(stderr, "Read %d frames\n", nframes);
 +
 +    if (bRef)
 +    {
 +        /* copy the reference structure to the ouput array x */
 +        snew(xproj, natoms);
 +        for (i = 0; i < natoms; i++)
 +        {
 +            copy_rvec(xref[index[i]], xproj[i]);
 +        }
 +    }
 +    else
 +    {
 +        xproj = xav;
 +    }
 +
 +    /* correct the covariance matrix for the mass */
 +    inv_nframes = 1.0/nframes;
 +    for (j = 0; j < natoms; j++)
 +    {
 +        for (dj = 0; dj < DIM; dj++)
 +        {
 +            for (i = j; i < natoms; i++)
 +            {
 +                k = ndim*(DIM*j+dj)+DIM*i;
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* symmetrize the matrix */
 +    for (j = 0; j < ndim; j++)
 +    {
 +        for (i = j; i < ndim; i++)
 +        {
 +            mat[ndim*i+j] = mat[ndim*j+i];
 +        }
 +    }
 +
 +    trace = 0;
 +    for (i = 0; i < ndim; i++)
 +    {
 +        trace += mat[i*ndim+i];
 +    }
 +    fprintf(stderr, "\nTrace of the covariance matrix: %g (%snm^2)\n",
 +            trace, bM ? "u " : "");
 +
 +    if (asciifile)
 +    {
 +        out = gmx_ffopen(asciifile, "w");
 +        for (j = 0; j < ndim; j++)
 +        {
 +            for (i = 0; i < ndim; i += 3)
 +            {
 +                fprintf(out, "%g %g %g\n",
 +                        mat[ndim*j+i], mat[ndim*j+i+1], mat[ndim*j+i+2]);
 +            }
 +        }
 +        gmx_ffclose(out);
 +    }
 +
 +    if (xpmfile)
 +    {
 +        min = 0;
 +        max = 0;
 +        snew(mat2, ndim);
 +        for (j = 0; j < ndim; j++)
 +        {
 +            mat2[j] = &(mat[ndim*j]);
 +            for (i = 0; i <= j; i++)
 +            {
 +                if (mat2[j][i] < min)
 +                {
 +                    min = mat2[j][i];
 +                }
 +                if (mat2[j][j] > max)
 +                {
 +                    max = mat2[j][i];
 +                }
 +            }
 +        }
 +        snew(axis, ndim);
 +        for (i = 0; i < ndim; i++)
 +        {
 +            axis[i] = i+1;
 +        }
 +        rlo.r   = 0; rlo.g = 0; rlo.b = 1;
 +        rmi.r   = 1; rmi.g = 1; rmi.b = 1;
 +        rhi.r   = 1; rhi.g = 0; rhi.b = 0;
 +        out     = gmx_ffopen(xpmfile, "w");
 +        nlevels = 80;
 +        write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
 +                   "dim", "dim", ndim, ndim, axis, axis,
 +                   mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
 +        gmx_ffclose(out);
 +        sfree(axis);
 +        sfree(mat2);
 +    }
 +
 +    if (xpmafile)
 +    {
 +        min = 0;
 +        max = 0;
 +        snew(mat2, ndim/DIM);
 +        for (i = 0; i < ndim/DIM; i++)
 +        {
 +            snew(mat2[i], ndim/DIM);
 +        }
 +        for (j = 0; j < ndim/DIM; j++)
 +        {
 +            for (i = 0; i <= j; i++)
 +            {
 +                mat2[j][i] = 0;
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
 +                }
 +                if (mat2[j][i] < min)
 +                {
 +                    min = mat2[j][i];
 +                }
 +                if (mat2[j][j] > max)
 +                {
 +                    max = mat2[j][i];
 +                }
 +                mat2[i][j] = mat2[j][i];
 +            }
 +        }
 +        snew(axis, ndim/DIM);
 +        for (i = 0; i < ndim/DIM; i++)
 +        {
 +            axis[i] = i+1;
 +        }
 +        rlo.r   = 0; rlo.g = 0; rlo.b = 1;
 +        rmi.r   = 1; rmi.g = 1; rmi.b = 1;
 +        rhi.r   = 1; rhi.g = 0; rhi.b = 0;
 +        out     = gmx_ffopen(xpmafile, "w");
 +        nlevels = 80;
 +        write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
 +                   "atom", "atom", ndim/DIM, ndim/DIM, axis, axis,
 +                   mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
 +        gmx_ffclose(out);
 +        sfree(axis);
 +        for (i = 0; i < ndim/DIM; i++)
 +        {
 +            sfree(mat2[i]);
 +        }
 +        sfree(mat2);
 +    }
 +
 +
 +    /* call diagonalization routine */
 +
 +    snew(eigenvalues, ndim);
 +    snew(eigenvectors, ndim*ndim);
 +
 +    memcpy(eigenvectors, mat, ndim*ndim*sizeof(real));
 +    fprintf(stderr, "\nDiagonalizing ...\n");
 +    fflush(stderr);
 +    eigensolver(eigenvectors, ndim, 0, ndim, eigenvalues, mat);
 +    sfree(eigenvectors);
 +
 +    /* now write the output */
 +
 +    sum = 0;
 +    for (i = 0; i < ndim; i++)
 +    {
 +        sum += eigenvalues[i];
 +    }
 +    fprintf(stderr, "\nSum of the eigenvalues: %g (%snm^2)\n",
 +            sum, bM ? "u " : "");
 +    if (fabs(trace-sum) > 0.01*trace)
 +    {
 +        fprintf(stderr, "\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
 +    }
 +
 +    fprintf(stderr, "\nWriting eigenvalues to %s\n", eigvalfile);
 +
 +    sprintf(str, "(%snm\\S2\\N)", bM ? "u " : "");
 +    out = xvgropen(eigvalfile,
 +                   "Eigenvalues of the covariance matrix",
 +                   "Eigenvector index", str, oenv);
-     fprintf(out, "Wrote %d eigenvalues to %s\n", (int)ndim, eigvalfile);
++    for (i = 0; (i < end); i++)
 +    {
 +        fprintf (out, "%10d %g\n", (int)i+1, eigenvalues[ndim-1-i]);
 +    }
 +    gmx_ffclose(out);
 +
 +    if (end == -1)
 +    {
 +        if (nframes-1 < ndim)
 +        {
 +            end = nframes-1;
++            fprintf(out, "WARNING: there are fewer frames in your trajectory than there are\n");
++            fprintf(out, "degrees of freedom in your system. Only generating the first\n");
++            fprintf(out, "%d out of %d eigenvectors and eigenvalues.\n", end, (int)ndim);
 +        }
 +        else
 +        {
 +            end = ndim;
 +        }
 +    }
 +    if (bFit)
 +    {
 +        /* misuse lambda: 0/1 mass weighted analysis no/yes */
 +        if (nfit == natoms)
 +        {
 +            WriteXref = eWXR_YES;
 +            for (i = 0; i < nfit; i++)
 +            {
 +                copy_rvec(xref[ifit[i]], x[i]);
 +            }
 +        }
 +        else
 +        {
 +            WriteXref = eWXR_NO;
 +        }
 +    }
 +    else
 +    {
 +        /* misuse lambda: -1 for no fit */
 +        WriteXref = eWXR_NOFIT;
 +    }
 +
 +    write_eigenvectors(eigvecfile, natoms, mat, TRUE, 1, end,
 +                       WriteXref, x, bDiffMass1, xproj, bM, eigenvalues);
 +
 +    out = gmx_ffopen(logfile, "w");
 +
 +    time(&now);
 +    gmx_ctime_r(&now, timebuf, STRLEN);
 +    fprintf(out, "Covariance analysis log, written %s\n", timebuf);
 +
 +    fprintf(out, "Program: %s\n", argv[0]);
 +    gmx_getcwd(str, STRLEN);
 +
 +    fprintf(out, "Working directory: %s\n\n", str);
 +
 +    fprintf(out, "Read %d frames from %s (time %g to %g %s)\n", nframes, trxfile,
 +            output_env_conv_time(oenv, tstart), output_env_conv_time(oenv, tend), output_env_get_time_unit(oenv));
 +    if (bFit)
 +    {
 +        fprintf(out, "Read reference structure for fit from %s\n", fitfile);
 +    }
 +    if (ndxfile)
 +    {
 +        fprintf(out, "Read index groups from %s\n", ndxfile);
 +    }
 +    fprintf(out, "\n");
 +
 +    fprintf(out, "Analysis group is '%s' (%d atoms)\n", ananame, natoms);
 +    if (bFit)
 +    {
 +        fprintf(out, "Fit group is '%s' (%d atoms)\n", fitname, nfit);
 +    }
 +    else
 +    {
 +        fprintf(out, "No fit was used\n");
 +    }
 +    fprintf(out, "Analysis is %smass weighted\n", bDiffMass2 ? "" : "non-");
 +    if (bFit)
 +    {
 +        fprintf(out, "Fit is %smass weighted\n", bDiffMass1 ? "" : "non-");
 +    }
 +    fprintf(out, "Diagonalized the %dx%d covariance matrix\n", (int)ndim, (int)ndim);
 +    fprintf(out, "Trace of the covariance matrix before diagonalizing: %g\n",
 +            trace);
 +    fprintf(out, "Trace of the covariance matrix after diagonalizing: %g\n\n",
 +            sum);
 +
++    fprintf(out, "Wrote %d eigenvalues to %s\n", (int)end, eigvalfile);
 +    if (WriteXref == eWXR_YES)
 +    {
 +        fprintf(out, "Wrote reference structure to %s\n", eigvecfile);
 +    }
 +    fprintf(out, "Wrote average structure to %s and %s\n", averfile, eigvecfile);
 +    fprintf(out, "Wrote eigenvectors %d to %d to %s\n", 1, end, eigvecfile);
 +
 +    gmx_ffclose(out);
 +
 +    fprintf(stderr, "Wrote the log to %s\n", logfile);
 +
 +    return 0;
 +}
index 3e29541e9eeda3cb426f9ed9e196542b02827d22,0000000000000000000000000000000000000000..7a9a689cc833cbdc61704f12eba7ec0906dc57dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,218 -1,0 +1,224 @@@
-         "from. One needs an energy file with the following components:",
-         "Coul (A-B) LJ-SR (A-B) etc."
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <math.h>
 +#include <string.h>
 +
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "gromacs/fileio/futil.h"
 +#include "txtdump.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "gstat.h"
 +#include "xvgr.h"
 +#include "gmx_ana.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +typedef struct {
 +    int  nlj, nqq;
 +    int *lj;
 +    int *qq;
 +} t_liedata;
 +
 +static t_liedata *analyze_names(int nre, gmx_enxnm_t *names, const char *ligand)
 +{
 +    int        i;
 +    t_liedata *ld;
 +    char       self[256];
 +
 +    /* Skip until we come to pressure */
 +    for (i = 0; (i < F_NRE); i++)
 +    {
 +        if (strcmp(names[i].name, interaction_function[F_PRES].longname) == 0)
 +        {
 +            break;
 +        }
 +    }
 +
 +    /* Now real analysis: find components of energies */
 +    sprintf(self, "%s-%s", ligand, ligand);
 +    snew(ld, 1);
 +    for (; (i < nre); i++)
 +    {
 +        if ((strstr(names[i].name, ligand) != NULL) &&
 +            (strstr(names[i].name, self) == NULL))
 +        {
 +            if (strstr(names[i].name, "LJ") != NULL)
 +            {
 +                ld->nlj++;
 +                srenew(ld->lj, ld->nlj);
 +                ld->lj[ld->nlj-1] = i;
 +            }
 +            else if (strstr(names[i].name, "Coul") != NULL)
 +            {
 +                ld->nqq++;
 +                srenew(ld->qq, ld->nqq);
 +                ld->qq[ld->nqq-1] = i;
 +            }
 +        }
 +    }
 +    printf("Using the following energy terms:\n");
 +    printf("LJ:  ");
 +    for (i = 0; (i < ld->nlj); i++)
 +    {
 +        printf("  %12s", names[ld->lj[i]].name);
 +    }
 +    printf("\nCoul:");
 +    for (i = 0; (i < ld->nqq); i++)
 +    {
 +        printf("  %12s", names[ld->qq[i]].name);
 +    }
 +    printf("\n");
 +
 +    return ld;
 +}
 +
 +real calc_lie(t_liedata *ld, t_energy ee[], real lie_lj, real lie_qq,
 +              real fac_lj, real fac_qq)
 +{
 +    int  i;
 +    real lj_tot, qq_tot;
 +
 +    lj_tot = 0;
 +    for (i = 0; (i < ld->nlj); i++)
 +    {
 +        lj_tot += ee[ld->lj[i]].e;
 +    }
 +    qq_tot = 0;
 +    for (i = 0; (i < ld->nqq); i++)
 +    {
 +        qq_tot += ee[ld->qq[i]].e;
 +    }
 +
 +    /* And now the great LIE formula: */
 +    return fac_lj*(lj_tot-lie_lj)+fac_qq*(qq_tot-lie_qq);
 +}
 +
 +int gmx_lie(int argc, char *argv[])
 +{
 +    const char        *desc[] = {
 +        "[THISMODULE] computes a free energy estimate based on an energy analysis",
++        "from nonbonded energies. One needs an energy file with the following components:",
++        "Coul-(A-B) LJ-SR (A-B) etc.[PAR]",
++        "To utilize [TT]g_lie[tt] correctly, two simulations are required: one with the",
++        "molecule of interest bound to its receptor and one with the molecule in water.",
++        "Both need to utilize [TT]energygrps[tt] such that Coul-SR(A-B), LJ-SR(A-B), etc. terms",
++        "are written to the [TT].edr[tt] file. Values from the molecule-in-water simulation",
++        "are necessary for supplying suitable values for -Elj and -Eqq."
 +    };
 +    static real        lie_lj = 0, lie_qq = 0, fac_lj = 0.181, fac_qq = 0.5;
 +    static const char *ligand = "none";
 +    t_pargs            pa[]   = {
 +        { "-Elj",  FALSE, etREAL, {&lie_lj},
 +          "Lennard-Jones interaction between ligand and solvent" },
 +        { "-Eqq",  FALSE, etREAL, {&lie_qq},
 +          "Coulomb interaction between ligand and solvent" },
 +        { "-Clj",  FALSE, etREAL, {&fac_lj},
 +          "Factor in the LIE equation for Lennard-Jones component of energy" },
 +        { "-Cqq",  FALSE, etREAL, {&fac_qq},
 +          "Factor in the LIE equation for Coulomb component of energy" },
 +        { "-ligand",  FALSE, etSTR, {&ligand},
 +          "Name of the ligand in the energy file" }
 +    };
 +#define NPA asize(pa)
 +
 +    FILE         *out;
 +    int           nre, nframes = 0, ct = 0;
 +    ener_file_t   fp;
 +    gmx_bool      bCont;
 +    t_liedata    *ld;
 +    gmx_enxnm_t  *enm = NULL;
 +    t_enxframe   *fr;
 +    real          lie;
 +    double        lieaver = 0, lieav2 = 0;
 +    output_env_t  oenv;
 +
 +    t_filenm      fnm[] = {
 +        { efEDR, "-f",    "ener",     ffREAD   },
 +        { efXVG, "-o",    "lie",      ffWRITE  }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    fp = open_enx(ftp2fn(efEDR, NFILE, fnm), "r");
 +    do_enxnms(fp, &nre, &enm);
 +
 +    ld = analyze_names(nre, enm, ligand);
 +    snew(fr, 1);
++
 +    out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "LIE free energy estimate",
 +                   "Time (ps)", "DGbind (kJ/mol)", oenv);
 +    while (do_enx(fp, fr))
 +    {
 +        ct = check_times(fr->t);
 +        if (ct == 0)
 +        {
 +            lie      = calc_lie(ld, fr->ener, lie_lj, lie_qq, fac_lj, fac_qq);
 +            lieaver += lie;
 +            lieav2  += lie*lie;
 +            nframes++;
 +            fprintf(out, "%10g  %10g\n", fr->t, lie);
 +        }
 +    }
 +    close_enx(fp);
 +    gmx_ffclose(out);
 +    fprintf(stderr, "\n");
 +
 +    if (nframes > 0)
 +    {
 +        printf("DGbind = %.3f (%.3f)\n", lieaver/nframes,
 +               sqrt(lieav2/nframes-sqr(lieaver/nframes)));
 +    }
 +
 +    do_view(oenv, ftp2fn(efXVG, NFILE, fnm), "-nxy");
 +
 +    return 0;
 +}
index e2d7a17e619ec75431ee630e49a3932dc65eb00a,0000000000000000000000000000000000000000..27a3b5b752d35180f8391b9a4ccccc75450cd397
mode 100644,000000..100644
--- /dev/null
@@@ -1,667 -1,0 +1,672 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "addconf.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdlib.h>
 +#include <string.h>
 +#include "vec.h"
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "types/commrec.h"
 +#include "force.h"
 +#include "names.h"
 +#include "nsgrid.h"
 +#include "mdatoms.h"
 +#include "nrnb.h"
 +#include "ns.h"
 +#include "mtop_util.h"
 +#include "chargegroup.h"
 +
 +static real box_margin;
 +
 +static real max_dist(rvec *x, real *r, int start, int end)
 +{
 +    real maxd;
 +    int  i, j;
 +
 +    maxd = 0;
 +    for (i = start; i < end; i++)
 +    {
 +        for (j = i+1; j < end; j++)
 +        {
 +            maxd = max(maxd, sqrt(distance2(x[i], x[j]))+0.5*(r[i]+r[j]));
 +        }
 +    }
 +
 +    return 0.5*maxd;
 +}
 +
 +static gmx_bool outside_box_minus_margin2(rvec x, matrix box)
 +{
 +    return ( (x[XX] < 2*box_margin) || (x[XX] > box[XX][XX]-2*box_margin) ||
 +             (x[YY] < 2*box_margin) || (x[YY] > box[YY][YY]-2*box_margin) ||
 +             (x[ZZ] < 2*box_margin) || (x[ZZ] > box[ZZ][ZZ]-2*box_margin) );
 +}
 +
 +static gmx_bool outside_box_plus_margin(rvec x, matrix box)
 +{
 +    return ( (x[XX] < -box_margin) || (x[XX] > box[XX][XX]+box_margin) ||
 +             (x[YY] < -box_margin) || (x[YY] > box[YY][YY]+box_margin) ||
 +             (x[ZZ] < -box_margin) || (x[ZZ] > box[ZZ][ZZ]+box_margin) );
 +}
 +
 +static int mark_res(int at, gmx_bool *mark, int natoms, t_atom *atom, int *nmark)
 +{
 +    int resind;
 +
 +    resind = atom[at].resind;
 +    while ( (at > 0) && (resind == atom[at-1].resind) )
 +    {
 +        at--;
 +    }
 +    while ( (at < natoms) && (resind == atom[at].resind) )
 +    {
 +        if (!mark[at])
 +        {
 +            mark[at] = TRUE;
 +            (*nmark)++;
 +        }
 +        at++;
 +    }
 +
 +    return at;
 +}
 +
 +static real find_max_real(int n, real radius[])
 +{
 +    int  i;
 +    real rmax;
 +
 +    rmax = 0;
 +    if (n > 0)
 +    {
 +        rmax = radius[0];
 +        for (i = 1; (i < n); i++)
 +        {
 +            rmax = max(rmax, radius[i]);
 +        }
 +    }
 +    return rmax;
 +}
 +
 +static void combine_atoms(t_atoms *ap, t_atoms *as,
 +                          rvec xp[], rvec *vp, rvec xs[], rvec *vs,
 +                          t_atoms **a_comb, rvec **x_comb, rvec **v_comb)
 +{
 +    t_atoms *ac;
 +    rvec    *xc, *vc = NULL;
 +    int      i, j, natot, res0;
 +
 +    /* Total number of atoms */
 +    natot = ap->nr+as->nr;
 +
 +    snew(ac, 1);
 +    init_t_atoms(ac, natot, FALSE);
 +
 +    snew(xc, natot);
 +    if (vp && vs)
 +    {
 +        snew(vc, natot);
 +    }
 +
 +    /* Fill the new structures */
 +    for (i = j = 0; (i < ap->nr); i++, j++)
 +    {
 +        copy_rvec(xp[i], xc[j]);
 +        if (vc)
 +        {
 +            copy_rvec(vp[i], vc[j]);
 +        }
 +        memcpy(&(ac->atom[j]), &(ap->atom[i]), sizeof(ap->atom[i]));
 +        ac->atom[j].type = 0;
 +    }
 +    res0 = ap->nres;
 +    for (i = 0; (i < as->nr); i++, j++)
 +    {
 +        copy_rvec(xs[i], xc[j]);
 +        if (vc)
 +        {
 +            copy_rvec(vs[i], vc[j]);
 +        }
 +        memcpy(&(ac->atom[j]), &(as->atom[i]), sizeof(as->atom[i]));
 +        ac->atom[j].type    = 0;
 +        ac->atom[j].resind += res0;
 +    }
 +    ac->nr   = j;
 +    ac->nres = ac->atom[j-1].resind+1;
 +    /* Fill all elements to prevent uninitialized memory */
 +    for (i = 0; i < ac->nr; i++)
 +    {
 +        ac->atom[i].m     = 1;
 +        ac->atom[i].q     = 0;
 +        ac->atom[i].mB    = 1;
 +        ac->atom[i].qB    = 0;
 +        ac->atom[i].type  = 0;
 +        ac->atom[i].typeB = 0;
 +        ac->atom[i].ptype = eptAtom;
 +    }
 +
 +    /* Return values */
 +    *a_comb = ac;
 +    *x_comb = xc;
 +    *v_comb = vc;
 +}
 +
 +static t_forcerec *fr = NULL;
 +
 +static void do_nsgrid(FILE *fp, gmx_bool bVerbose,
 +                      matrix box, rvec x[], t_atoms *atoms, real rlong,
 +                      const output_env_t oenv)
 +{
 +    gmx_mtop_t     *mtop;
 +    gmx_localtop_t *top;
 +    t_mdatoms      *md;
 +    t_block        *cgs;
 +    t_inputrec     *ir;
 +    t_nrnb          nrnb;
 +    t_commrec      *cr;
 +    int            *cg_index;
 +    gmx_moltype_t  *molt;
 +    gmx_ffparams_t *ffp;
 +    ivec           *nFreeze;
 +    int             i, m, natoms;
 +    rvec            box_size;
 +    real           *lambda, *dvdl;
 +
 +    natoms = atoms->nr;
 +
 +    /* Charge group index */
 +    snew(cg_index, natoms);
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        cg_index[i] = i;
 +    }
 +
 +    /* Topology needs charge groups and exclusions */
 +    snew(mtop, 1);
 +    init_mtop(mtop);
 +    mtop->natoms = natoms;
 +    /* Make one moltype that contains the whol system */
 +    mtop->nmoltype = 1;
 +    snew(mtop->moltype, mtop->nmoltype);
 +    molt        = &mtop->moltype[0];
 +    molt->name  = mtop->name;
 +    molt->atoms = *atoms;
 +    stupid_fill_block(&molt->cgs, mtop->natoms, FALSE);
 +    stupid_fill_blocka(&molt->excls, natoms);
 +    /* Make one molblock for the whole system */
 +    mtop->nmolblock = 1;
 +    snew(mtop->molblock, mtop->nmolblock);
 +    mtop->molblock[0].type       = 0;
 +    mtop->molblock[0].nmol       = 1;
 +    mtop->molblock[0].natoms_mol = natoms;
 +    /* Initialize a single energy group */
 +    mtop->groups.grps[egcENER].nr = 1;
 +    mtop->groups.ngrpnr[egcENER]  = 0;
 +    mtop->groups.grpnr[egcENER]   = NULL;
 +
 +    ffp = &mtop->ffparams;
 +
 +    ffp->ntypes = 1;
 +    ffp->atnr   = 1;
 +    ffp->reppow = 12;
 +    snew(ffp->functype, 1);
 +    snew(ffp->iparams, 1);
 +    ffp->iparams[0].lj.c6  = 1;
 +    ffp->iparams[0].lj.c12 = 1;
 +
 +    /* inputrec structure */
 +    snew(ir, 1);
 +    ir->cutoff_scheme    = ecutsGROUP;
 +    ir->coulomb_modifier = eintmodNONE;
 +    ir->vdw_modifier     = eintmodNONE;
 +    ir->coulombtype      = eelCUT;
 +    ir->vdwtype          = evdwCUT;
 +    ir->ndelta           = 2;
 +    ir->ns_type          = ensGRID;
 +    snew(ir->opts.egp_flags, 1);
 +
 +    top = gmx_mtop_generate_local_top(mtop, ir);
 +
 +    /* Some nasty shortcuts */
 +    cgs  = &(top->cgs);
 +
 +    /* mdatoms structure */
 +    snew(nFreeze, 2);
 +    snew(md, 1);
 +    md = init_mdatoms(fp, mtop, FALSE);
 +    atoms2md(mtop, ir, 0, NULL, mtop->natoms, md);
 +    sfree(nFreeze);
 +
 +    /* forcerec structure */
 +    if (fr == NULL)
 +    {
 +        fr = mk_forcerec();
 +    }
 +    snew(cr, 1);
 +    cr->nnodes   = 1;
 +    /* cr->nthreads = 1; */
 +
 +    /*    ir->rlist       = ir->rcoulomb = ir->rvdw = rlong;
 +       printf("Neighborsearching with a cut-off of %g\n",rlong);
 +       init_forcerec(stdout,fr,ir,top,cr,md,box,FALSE,NULL,NULL,NULL,TRUE);*/
 +    fr->cg0     = 0;
 +    fr->hcg     = top->cgs.nr;
 +    fr->nWatMol = 0;
 +
 +    /* Prepare for neighboursearching */
 +    init_nrnb(&nrnb);
 +
 +    /* Init things dependent on parameters */
 +    ir->rlistlong = ir->rlist = ir->rcoulomb = ir->rvdw = rlong;
 +    /* create free energy data to avoid NULLs */
 +    snew(ir->fepvals, 1);
 +    printf("Neighborsearching with a cut-off of %g\n", rlong);
 +    init_forcerec(stdout, oenv, fr, NULL, ir, mtop, cr, box,
 +                  NULL, NULL, NULL, NULL, NULL, TRUE, -1);
 +    if (debug)
 +    {
 +        pr_forcerec(debug, fr);
 +    }
 +
 +    /* Calculate new stuff dependent on coords and box */
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        box_size[m] = box[m][m];
 +    }
 +    calc_shifts(box, fr->shift_vec);
 +    put_charge_groups_in_box(fp, 0, cgs->nr, fr->ePBC, box, cgs, x, fr->cg_cm);
 +
 +    /* Do the actual neighboursearching */
 +    snew(lambda, efptNR);
 +    snew(dvdl, efptNR);
 +    init_neighbor_list(fp, fr, md->homenr);
 +    search_neighbours(fp, fr, box, top,
 +                      &mtop->groups, cr, &nrnb, md, TRUE, FALSE);
 +
 +    if (debug)
 +    {
 +        dump_nblist(debug, cr, fr, 0);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Successfully made neighbourlist\n");
 +    }
 +}
 +
 +static gmx_bool bXor(gmx_bool b1, gmx_bool b2)
 +{
 +    return (b1 && !b2) || (b2 && !b1);
 +}
 +
 +void add_conf(t_atoms *atoms, rvec **x, rvec **v, real **r, gmx_bool bSrenew,
 +              int ePBC, matrix box, gmx_bool bInsert,
 +              t_atoms *atoms_solvt, rvec *x_solvt, rvec *v_solvt, real *r_solvt,
 +              gmx_bool bVerbose, real rshell, int max_sol, const output_env_t oenv)
 +{
 +    t_nblist       *nlist;
 +    t_atoms        *atoms_all;
 +    real            max_vdw, *r_prot, *r_all, n2, r2, ib1, ib2;
 +    int             natoms_prot, natoms_solvt;
 +    int             i, j, jj, m, j0, j1, jjj, jnres, jnr, inr, iprot, is1, is2;
 +    int             prev, resnr, nresadd, d, k, ncells, maxincell;
 +    int             dx0, dx1, dy0, dy1, dz0, dz1;
 +    int             ntest, nremove, nkeep;
 +    rvec            dx, xi, xj, xpp, *x_all, *v_all;
 +    gmx_bool       *remove, *keep;
 +    int             bSolSol;
 +
 +    natoms_prot  = atoms->nr;
 +    natoms_solvt = atoms_solvt->nr;
 +    if (natoms_solvt <= 0)
 +    {
 +        fprintf(stderr, "WARNING: Nothing to add\n");
 +        return;
 +    }
 +
 +    if (ePBC == epbcSCREW)
 +    {
 +        gmx_fatal(FARGS, "Sorry, %s pbc is not yet supported", epbc_names[ePBC]);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Calculating Overlap...\n");
 +    }
 +
 +    /* Set margin around box edges to largest solvent dimension.
 +     * The maximum distance between atoms in a solvent molecule should
 +     * be calculated. At the moment a fudge factor of 3 is used.
 +     */
 +    r_prot     = *r;
 +    box_margin = 3*find_max_real(natoms_solvt, r_solvt);
 +    max_vdw    = max(3*find_max_real(natoms_prot, r_prot), box_margin);
 +    fprintf(stderr, "box_margin = %g\n", box_margin);
 +
 +    snew(remove, natoms_solvt);
 +
 +    nremove = 0;
 +    if (!bInsert)
 +    {
 +        for (i = 0; i < atoms_solvt->nr; i++)
 +        {
 +            if (outside_box_plus_margin(x_solvt[i], box) )
 +            {
 +                i = mark_res(i, remove, atoms_solvt->nr, atoms_solvt->atom, &nremove);
 +            }
 +        }
 +        fprintf(stderr, "Removed %d atoms that were outside the box\n", nremove);
 +    }
 +
 +    /* Define grid stuff */
 +    /* Largest VDW radius */
 +    snew(r_all, natoms_prot+natoms_solvt);
 +    for (i = j = 0; i < natoms_prot; i++, j++)
 +    {
 +        r_all[j] = r_prot[i];
 +    }
 +    for (i = 0; i < natoms_solvt; i++, j++)
 +    {
 +        r_all[j] = r_solvt[i];
 +    }
 +
 +    /* Combine arrays */
 +    combine_atoms(atoms, atoms_solvt, *x, v ? *v : NULL, x_solvt, v_solvt,
 +                  &atoms_all, &x_all, &v_all);
 +
 +    /* Do neighboursearching step */
 +    do_nsgrid(stdout, bVerbose, box, x_all, atoms_all, max_vdw, oenv);
 +
 +    /* check solvent with solute */
 +    nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]);
 +    fprintf(stderr, "nri = %d, nrj = %d\n", nlist->nri, nlist->nrj);
 +    for (bSolSol = 0; (bSolSol <= (bInsert ? 0 : 1)); bSolSol++)
 +    {
 +        ntest = nremove = 0;
 +        fprintf(stderr, "Checking %s-Solvent overlap:",
 +                bSolSol ? "Solvent" : "Protein");
 +        for (i = 0; (i < nlist->nri && nremove < natoms_solvt); i++)
 +        {
 +            inr = nlist->iinr[i];
 +            j0  = nlist->jindex[i];
 +            j1  = nlist->jindex[i+1];
 +            rvec_add(x_all[inr], fr->shift_vec[nlist->shift[i]], xi);
 +
 +            for (j = j0; (j < j1 && nremove < natoms_solvt); j++)
 +            {
 +                jnr = nlist->jjnr[j];
++                if (jnr < 0)
++                {
++                    /* skip padding */
++                    continue;
++                }
 +                copy_rvec(x_all[jnr], xj);
 +
 +                /* Check solvent-protein and solvent-solvent */
 +                is1 = inr-natoms_prot;
 +                is2 = jnr-natoms_prot;
 +
 +                /* Check if at least one of the atoms is a solvent that is not yet
 +                 * listed for removal, and if both are solvent, that they are not in the
 +                 * same residue.
 +                 */
 +                if ((!bSolSol &&
 +                     bXor((is1 >= 0), (is2 >= 0)) && /* One atom is protein */
 +                     ((is1 < 0) || ((is1 >= 0) && !remove[is1])) &&
 +                     ((is2 < 0) || ((is2 >= 0) && !remove[is2]))) ||
 +
 +                    (bSolSol  &&
 +                     (is1 >= 0) && (!remove[is1]) &&                   /* is1 is solvent */
 +                     (is2 >= 0) && (!remove[is2]) &&                   /* is2 is solvent */
 +                     (bInsert ||                                       /* when inserting also check inside the box */
 +                      (outside_box_minus_margin2(x_solvt[is1], box) && /* is1 on edge */
 +                       outside_box_minus_margin2(x_solvt[is2], box))   /* is2 on edge */
 +                     ) &&
 +                     (atoms_solvt->atom[is1].resind !=                 /* Not the same residue */
 +                      atoms_solvt->atom[is2].resind)))
 +                {
 +
 +                    ntest++;
 +                    rvec_sub(xi, xj, dx);
 +                    n2 = norm2(dx);
 +                    r2 = sqr(r_all[inr]+r_all[jnr]);
 +                    if (n2 < r2)
 +                    {
 +                        if (bInsert)
 +                        {
 +                            nremove = natoms_solvt;
 +                            for (k = 0; k < nremove; k++)
 +                            {
 +                                remove[k] = TRUE;
 +                            }
 +                        }
 +                        /* Need only remove one of the solvents... */
 +                        if (is2 >= 0)
 +                        {
 +                            (void) mark_res(is2, remove, natoms_solvt, atoms_solvt->atom,
 +                                            &nremove);
 +                        }
 +                        else if (is1 >= 0)
 +                        {
 +                            (void) mark_res(is1, remove, natoms_solvt, atoms_solvt->atom,
 +                                            &nremove);
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "Neither atom is solvent%d %d\n", is1, is2);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        if (!bInsert)
 +        {
 +            fprintf(stderr, " tested %d pairs, removed %d atoms.\n", ntest, nremove);
 +        }
 +    }
 +    if (debug)
 +    {
 +        for (i = 0; i < natoms_solvt; i++)
 +        {
 +            fprintf(debug, "remove[%5d] = %s\n", i, bool_names[remove[i]]);
 +        }
 +    }
 +
 +    /* Search again, now with another cut-off */
 +    if (rshell > 0)
 +    {
 +        do_nsgrid(stdout, bVerbose, box, x_all, atoms_all, rshell, oenv);
 +        nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]);
 +        fprintf(stderr, "nri = %d, nrj = %d\n", nlist->nri, nlist->nrj);
 +        nkeep = 0;
 +        snew(keep, natoms_solvt);
 +        for (i = 0; i < nlist->nri; i++)
 +        {
 +            inr = nlist->iinr[i];
 +            j0  = nlist->jindex[i];
 +            j1  = nlist->jindex[i+1];
 +
 +            for (j = j0; j < j1; j++)
 +            {
 +                jnr = nlist->jjnr[j];
 +
 +                /* Check solvent-protein and solvent-solvent */
 +                is1 = inr-natoms_prot;
 +                is2 = jnr-natoms_prot;
 +
 +                /* Check if at least one of the atoms is a solvent that is not yet
 +                 * listed for removal, and if both are solvent, that they are not in the
 +                 * same residue.
 +                 */
 +                if (is1 >= 0 && is2 < 0)
 +                {
 +                    mark_res(is1, keep, natoms_solvt, atoms_solvt->atom, &nkeep);
 +                }
 +                else if (is1 < 0 && is2 >= 0)
 +                {
 +                    mark_res(is2, keep, natoms_solvt, atoms_solvt->atom, &nkeep);
 +                }
 +            }
 +        }
 +        fprintf(stderr, "Keeping %d solvent atoms after proximity check\n",
 +                nkeep);
 +        for (i = 0; i < natoms_solvt; i++)
 +        {
 +            remove[i] = remove[i] || !keep[i];
 +        }
 +        sfree(keep);
 +    }
 +    /* count how many atoms and residues will be added and make space */
 +    if (bInsert)
 +    {
 +        j     = atoms_solvt->nr;
 +        jnres = atoms_solvt->nres;
 +    }
 +    else
 +    {
 +        j     = 0;
 +        jnres = 0;
 +        for (i = 0; ((i < atoms_solvt->nr) &&
 +                     ((max_sol == 0) || (jnres < max_sol))); i++)
 +        {
 +            if (!remove[i])
 +            {
 +                j++;
 +                if ((i == 0) ||
 +                    (atoms_solvt->atom[i].resind != atoms_solvt->atom[i-1].resind))
 +                {
 +                    jnres++;
 +                }
 +            }
 +        }
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Will add %d atoms in %d residues\n", j, jnres);
 +    }
 +    if (!bInsert)
 +    {
 +        /* Flag the remaing solvent atoms to be removed */
 +        jjj = atoms_solvt->atom[i-1].resind;
 +        for (; (i < atoms_solvt->nr); i++)
 +        {
 +            if (atoms_solvt->atom[i].resind > jjj)
 +            {
 +                remove[i] = TRUE;
 +            }
 +            else
 +            {
 +                j++;
 +            }
 +        }
 +    }
 +
 +    if (bSrenew)
 +    {
 +        srenew(atoms->resinfo,  atoms->nres+jnres);
 +        srenew(atoms->atomname, atoms->nr+j);
 +        srenew(atoms->atom,     atoms->nr+j);
 +        srenew(*x,              atoms->nr+j);
 +        if (v)
 +        {
 +            srenew(*v,       atoms->nr+j);
 +        }
 +        srenew(*r,              atoms->nr+j);
 +    }
 +
 +    /* add the selected atoms_solvt to atoms */
 +    if (atoms->nr > 0)
 +    {
 +        resnr = atoms->resinfo[atoms->atom[atoms->nr-1].resind].nr;
 +    }
 +    else
 +    {
 +        resnr = 0;
 +    }
 +    prev    = -1;
 +    nresadd = 0;
 +    for (i = 0; i < atoms_solvt->nr; i++)
 +    {
 +        if (!remove[i])
 +        {
 +            if (prev == -1 ||
 +                atoms_solvt->atom[i].resind != atoms_solvt->atom[prev].resind)
 +            {
 +                nresadd++;
 +                atoms->nres++;
 +                resnr++;
 +                atoms->resinfo[atoms->nres-1] =
 +                    atoms_solvt->resinfo[atoms_solvt->atom[i].resind];
 +                atoms->resinfo[atoms->nres-1].nr = resnr;
 +                /* calculate shift of the solvent molecule using the first atom */
 +                copy_rvec(x_solvt[i], dx);
 +                put_atoms_in_box(ePBC, box, 1, &dx);
 +                rvec_dec(dx, x_solvt[i]);
 +            }
 +            atoms->atom[atoms->nr]     = atoms_solvt->atom[i];
 +            atoms->atomname[atoms->nr] = atoms_solvt->atomname[i];
 +            rvec_add(x_solvt[i], dx, (*x)[atoms->nr]);
 +            if (v)
 +            {
 +                copy_rvec(v_solvt[i], (*v)[atoms->nr]);
 +            }
 +            (*r)[atoms->nr]               = r_solvt[i];
 +            atoms->atom[atoms->nr].resind = atoms->nres-1;
 +            atoms->nr++;
 +            prev = i;
 +        }
 +    }
 +    if (bSrenew)
 +    {
 +        srenew(atoms->resinfo,  atoms->nres+nresadd);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Added %d molecules\n", nresadd);
 +    }
 +
 +    sfree(remove);
 +    done_atom(atoms_all);
 +    sfree(x_all);
 +    sfree(v_all);
 +}
index 872c670990c99807997386445ec424ecab86cc7e,0000000000000000000000000000000000000000..b562b5188fd5bc4dbdcb96297521fd9558395e56
mode 100644,000000..100644
--- /dev/null
@@@ -1,2054 -1,0 +1,2059 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "grompp.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <sys/types.h>
 +#include <math.h>
 +#include <string.h>
 +#include <errno.h>
 +#include <limits.h>
 +#include <assert.h>
 +
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "readir.h"
 +#include "toputil.h"
 +#include "topio.h"
 +#include "gromacs/fileio/confio.h"
 +#include "readir.h"
 +#include "symtab.h"
 +#include "names.h"
 +#include "grompp-impl.h"
 +#include "gromacs/random/random.h"
 +#include "gromacs/gmxpreprocess/gen_maxwell_velocities.h"
 +#include "vec.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "splitter.h"
 +#include "gromacs/gmxpreprocess/sortwater.h"
 +#include "convparm.h"
 +#include "gmx_fatal.h"
 +#include "warninp.h"
 +#include "index.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "vsite_parm.h"
 +#include "txtdump.h"
 +#include "calcgrid.h"
 +#include "add_par.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "perf_est.h"
 +#include "compute_io.h"
 +#include "gpp_atomtype.h"
 +#include "mtop_util.h"
 +#include "genborn.h"
 +#include "calc_verletbuf.h"
 +#include "tomorse.h"
 +#include "gromacs/imd/imd.h"
 +
 +
 +static int rm_interactions(int ifunc, int nrmols, t_molinfo mols[])
 +{
 +    int  i, n;
 +
 +    n = 0;
 +    /* For all the molecule types */
 +    for (i = 0; i < nrmols; i++)
 +    {
 +        n += mols[i].plist[ifunc].nr;
 +        mols[i].plist[ifunc].nr = 0;
 +    }
 +    return n;
 +}
 +
 +static int check_atom_names(const char *fn1, const char *fn2,
 +                            gmx_mtop_t *mtop, t_atoms *at)
 +{
 +    int      mb, m, i, j, nmismatch;
 +    t_atoms *tat;
 +#define MAXMISMATCH 20
 +
 +    if (mtop->natoms != at->nr)
 +    {
 +        gmx_incons("comparing atom names");
 +    }
 +
 +    nmismatch = 0;
 +    i         = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        tat = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +        for (m = 0; m < mtop->molblock[mb].nmol; m++)
 +        {
 +            for (j = 0; j < tat->nr; j++)
 +            {
 +                if (strcmp( *(tat->atomname[j]), *(at->atomname[i]) ) != 0)
 +                {
 +                    if (nmismatch < MAXMISMATCH)
 +                    {
 +                        fprintf(stderr,
 +                                "Warning: atom name %d in %s and %s does not match (%s - %s)\n",
 +                                i+1, fn1, fn2, *(tat->atomname[j]), *(at->atomname[i]));
 +                    }
 +                    else if (nmismatch == MAXMISMATCH)
 +                    {
 +                        fprintf(stderr, "(more than %d non-matching atom names)\n", MAXMISMATCH);
 +                    }
 +                    nmismatch++;
 +                }
 +                i++;
 +            }
 +        }
 +    }
 +
 +    return nmismatch;
 +}
 +
 +static void check_eg_vs_cg(gmx_mtop_t *mtop)
 +{
 +    int            astart, mb, m, cg, j, firstj;
 +    unsigned char  firsteg, eg;
 +    gmx_moltype_t *molt;
 +
 +    /* Go through all the charge groups and make sure all their
 +     * atoms are in the same energy group.
 +     */
 +
 +    astart = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +        for (m = 0; m < mtop->molblock[mb].nmol; m++)
 +        {
 +            for (cg = 0; cg < molt->cgs.nr; cg++)
 +            {
 +                /* Get the energy group of the first atom in this charge group */
 +                firstj  = astart + molt->cgs.index[cg];
 +                firsteg = ggrpnr(&mtop->groups, egcENER, firstj);
 +                for (j = molt->cgs.index[cg]+1; j < molt->cgs.index[cg+1]; j++)
 +                {
 +                    eg = ggrpnr(&mtop->groups, egcENER, astart+j);
 +                    if (eg != firsteg)
 +                    {
 +                        gmx_fatal(FARGS, "atoms %d and %d in charge group %d of molecule type '%s' are in different energy groups",
 +                                  firstj+1, astart+j+1, cg+1, *molt->name);
 +                    }
 +                }
 +            }
 +            astart += molt->atoms.nr;
 +        }
 +    }
 +}
 +
 +static void check_cg_sizes(const char *topfn, t_block *cgs, warninp_t wi)
 +{
 +    int  maxsize, cg;
 +    char warn_buf[STRLEN];
 +
 +    maxsize = 0;
 +    for (cg = 0; cg < cgs->nr; cg++)
 +    {
 +        maxsize = max(maxsize, cgs->index[cg+1]-cgs->index[cg]);
 +    }
 +
 +    if (maxsize > MAX_CHARGEGROUP_SIZE)
 +    {
 +        gmx_fatal(FARGS, "The largest charge group contains %d atoms. The maximum is %d.", maxsize, MAX_CHARGEGROUP_SIZE);
 +    }
 +    else if (maxsize > 10)
 +    {
 +        set_warning_line(wi, topfn, -1);
 +        sprintf(warn_buf,
 +                "The largest charge group contains %d atoms.\n"
 +                "Since atoms only see each other when the centers of geometry of the charge groups they belong to are within the cut-off distance, too large charge groups can lead to serious cut-off artifacts.\n"
 +                "For efficiency and accuracy, charge group should consist of a few atoms.\n"
 +                "For all-atom force fields use: CH3, CH2, CH, NH2, NH, OH, CO2, CO, etc.",
 +                maxsize);
 +        warning_note(wi, warn_buf);
 +    }
 +}
 +
 +static void check_bonds_timestep(gmx_mtop_t *mtop, double dt, warninp_t wi)
 +{
 +    /* This check is not intended to ensure accurate integration,
 +     * rather it is to signal mistakes in the mdp settings.
 +     * A common mistake is to forget to turn on constraints
 +     * for MD after energy minimization with flexible bonds.
 +     * This check can also detect too large time steps for flexible water
 +     * models, but such errors will often be masked by the constraints
 +     * mdp options, which turns flexible water into water with bond constraints,
 +     * but without an angle constraint. Unfortunately such incorrect use
 +     * of water models can not easily be detected without checking
 +     * for specific model names.
 +     *
 +     * The stability limit of leap-frog or velocity verlet is 4.44 steps
 +     * per oscillational period.
 +     * But accurate bonds distributions are lost far before that limit.
 +     * To allow relatively common schemes (although not common with Gromacs)
 +     * of dt=1 fs without constraints and dt=2 fs with only H-bond constraints
 +     * we set the note limit to 10.
 +     */
 +    int            min_steps_warn = 5;
 +    int            min_steps_note = 10;
 +    t_iparams     *ip;
 +    int            molt;
 +    gmx_moltype_t *moltype, *w_moltype;
 +    t_atom        *atom;
 +    t_ilist       *ilist, *ilb, *ilc, *ils;
 +    int            ftype;
 +    int            i, a1, a2, w_a1, w_a2, j;
 +    real           twopi2, limit2, fc, re, m1, m2, period2, w_period2;
 +    gmx_bool       bFound, bWater, bWarn;
 +    char           warn_buf[STRLEN];
 +
 +    ip = mtop->ffparams.iparams;
 +
 +    twopi2 = sqr(2*M_PI);
 +
 +    limit2 = sqr(min_steps_note*dt);
 +
 +    w_a1      = w_a2 = -1;
 +    w_period2 = -1.0;
 +
 +    w_moltype = NULL;
 +    for (molt = 0; molt < mtop->nmoltype; molt++)
 +    {
 +        moltype = &mtop->moltype[molt];
 +        atom    = moltype->atoms.atom;
 +        ilist   = moltype->ilist;
 +        ilc     = &ilist[F_CONSTR];
 +        ils     = &ilist[F_SETTLE];
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (!(ftype == F_BONDS || ftype == F_G96BONDS || ftype == F_HARMONIC))
 +            {
 +                continue;
 +            }
 +
 +            ilb = &ilist[ftype];
 +            for (i = 0; i < ilb->nr; i += 3)
 +            {
 +                fc = ip[ilb->iatoms[i]].harmonic.krA;
 +                re = ip[ilb->iatoms[i]].harmonic.rA;
 +                if (ftype == F_G96BONDS)
 +                {
 +                    /* Convert squared sqaure fc to harmonic fc */
 +                    fc = 2*fc*re;
 +                }
 +                a1 = ilb->iatoms[i+1];
 +                a2 = ilb->iatoms[i+2];
 +                m1 = atom[a1].m;
 +                m2 = atom[a2].m;
 +                if (fc > 0 && m1 > 0 && m2 > 0)
 +                {
 +                    period2 = twopi2*m1*m2/((m1 + m2)*fc);
 +                }
 +                else
 +                {
 +                    period2 = GMX_FLOAT_MAX;
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "fc %g m1 %g m2 %g period %g\n",
 +                            fc, m1, m2, sqrt(period2));
 +                }
 +                if (period2 < limit2)
 +                {
 +                    bFound = FALSE;
 +                    for (j = 0; j < ilc->nr; j += 3)
 +                    {
 +                        if ((ilc->iatoms[j+1] == a1 && ilc->iatoms[j+2] == a2) ||
 +                            (ilc->iatoms[j+1] == a2 && ilc->iatoms[j+2] == a1))
 +                        {
 +                            bFound = TRUE;
 +                        }
 +                    }
 +                    for (j = 0; j < ils->nr; j += 4)
 +                    {
 +                        if ((a1 == ils->iatoms[j+1] || a1 == ils->iatoms[j+2] || a1 == ils->iatoms[j+3]) &&
 +                            (a2 == ils->iatoms[j+1] || a2 == ils->iatoms[j+2] || a2 == ils->iatoms[j+3]))
 +                        {
 +                            bFound = TRUE;
 +                        }
 +                    }
 +                    if (!bFound &&
 +                        (w_moltype == NULL || period2 < w_period2))
 +                    {
 +                        w_moltype = moltype;
 +                        w_a1      = a1;
 +                        w_a2      = a2;
 +                        w_period2 = period2;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (w_moltype != NULL)
 +    {
 +        bWarn = (w_period2 < sqr(min_steps_warn*dt));
 +        /* A check that would recognize most water models */
 +        bWater = ((*w_moltype->atoms.atomname[0])[0] == 'O' &&
 +                  w_moltype->atoms.nr <= 5);
 +        sprintf(warn_buf, "The bond in molecule-type %s between atoms %d %s and %d %s has an estimated oscillational period of %.1e ps, which is less than %d times the time step of %.1e ps.\n"
 +                "%s",
 +                *w_moltype->name,
 +                w_a1+1, *w_moltype->atoms.atomname[w_a1],
 +                w_a2+1, *w_moltype->atoms.atomname[w_a2],
 +                sqrt(w_period2), bWarn ? min_steps_warn : min_steps_note, dt,
 +                bWater ?
 +                "Maybe you asked for fexible water." :
 +                "Maybe you forgot to change the constraints mdp option.");
 +        if (bWarn)
 +        {
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static void check_vel(gmx_mtop_t *mtop, rvec v[])
 +{
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     a;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        if (atom->ptype == eptShell ||
 +            atom->ptype == eptBond  ||
 +            atom->ptype == eptVSite)
 +        {
 +            clear_rvec(v[a]);
 +        }
 +    }
 +}
 +
 +static gmx_bool nint_ftype(gmx_mtop_t *mtop, t_molinfo *mi, int ftype)
 +{
 +    int nint, mb;
 +
 +    nint = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        nint += mtop->molblock[mb].nmol*mi[mtop->molblock[mb].type].plist[ftype].nr;
 +    }
 +
 +    return nint;
 +}
 +
 +/* This routine reorders the molecule type array
 + * in the order of use in the molblocks,
 + * unused molecule types are deleted.
 + */
 +static void renumber_moltypes(gmx_mtop_t *sys,
 +                              int *nmolinfo, t_molinfo **molinfo)
 +{
 +    int       *order, norder, i;
 +    int        mb, mi;
 +    t_molinfo *minew;
 +
 +    snew(order, *nmolinfo);
 +    norder = 0;
 +    for (mb = 0; mb < sys->nmolblock; mb++)
 +    {
 +        for (i = 0; i < norder; i++)
 +        {
 +            if (order[i] == sys->molblock[mb].type)
 +            {
 +                break;
 +            }
 +        }
 +        if (i == norder)
 +        {
 +            /* This type did not occur yet, add it */
 +            order[norder] = sys->molblock[mb].type;
 +            /* Renumber the moltype in the topology */
 +            norder++;
 +        }
 +        sys->molblock[mb].type = i;
 +    }
 +
 +    /* We still need to reorder the molinfo structs */
 +    snew(minew, norder);
 +    for (mi = 0; mi < *nmolinfo; mi++)
 +    {
 +        for (i = 0; i < norder; i++)
 +        {
 +            if (order[i] == mi)
 +            {
 +                break;
 +            }
 +        }
 +        if (i == norder)
 +        {
 +            done_mi(&(*molinfo)[mi]);
 +        }
 +        else
 +        {
 +            minew[i] = (*molinfo)[mi];
 +        }
 +    }
 +    sfree(*molinfo);
 +
 +    *nmolinfo = norder;
 +    *molinfo  = minew;
 +}
 +
 +static void molinfo2mtop(int nmi, t_molinfo *mi, gmx_mtop_t *mtop)
 +{
 +    int            m;
 +    gmx_moltype_t *molt;
 +
 +    mtop->nmoltype = nmi;
 +    snew(mtop->moltype, nmi);
 +    for (m = 0; m < nmi; m++)
 +    {
 +        molt        = &mtop->moltype[m];
 +        molt->name  = mi[m].name;
 +        molt->atoms = mi[m].atoms;
 +        /* ilists are copied later */
 +        molt->cgs   = mi[m].cgs;
 +        molt->excls = mi[m].excls;
 +    }
 +}
 +
 +static void
 +new_status(const char *topfile, const char *topppfile, const char *confin,
 +           t_gromppopts *opts, t_inputrec *ir, gmx_bool bZero,
 +           gmx_bool bGenVel, gmx_bool bVerbose, t_state *state,
 +           gpp_atomtype_t atype, gmx_mtop_t *sys,
 +           int *nmi, t_molinfo **mi, t_params plist[],
 +           int *comb, double *reppow, real *fudgeQQ,
 +           gmx_bool bMorse,
 +           warninp_t wi)
 +{
 +    t_molinfo      *molinfo = NULL;
 +    int             nmolblock;
 +    gmx_molblock_t *molblock, *molbs;
 +    t_atoms        *confat;
 +    int             mb, i, nrmols, nmismatch;
 +    char            buf[STRLEN];
 +    gmx_bool        bGB = FALSE;
 +    char            warn_buf[STRLEN];
 +
 +    init_mtop(sys);
 +
 +    /* Set gmx_boolean for GB */
 +    if (ir->implicit_solvent)
 +    {
 +        bGB = TRUE;
 +    }
 +
 +    /* TOPOLOGY processing */
 +    sys->name = do_top(bVerbose, topfile, topppfile, opts, bZero, &(sys->symtab),
 +                       plist, comb, reppow, fudgeQQ,
 +                       atype, &nrmols, &molinfo, ir,
 +                       &nmolblock, &molblock, bGB,
 +                       wi);
 +
 +    sys->nmolblock = 0;
 +    snew(sys->molblock, nmolblock);
 +
 +    sys->natoms = 0;
 +    for (mb = 0; mb < nmolblock; mb++)
 +    {
 +        if (sys->nmolblock > 0 &&
 +            molblock[mb].type == sys->molblock[sys->nmolblock-1].type)
 +        {
 +            /* Merge consecutive blocks with the same molecule type */
 +            sys->molblock[sys->nmolblock-1].nmol += molblock[mb].nmol;
 +            sys->natoms += molblock[mb].nmol*sys->molblock[sys->nmolblock-1].natoms_mol;
 +        }
 +        else if (molblock[mb].nmol > 0)
 +        {
 +            /* Add a new molblock to the topology */
 +            molbs             = &sys->molblock[sys->nmolblock];
 +            *molbs            = molblock[mb];
 +            molbs->natoms_mol = molinfo[molbs->type].atoms.nr;
 +            molbs->nposres_xA = 0;
 +            molbs->nposres_xB = 0;
 +            sys->natoms      += molbs->nmol*molbs->natoms_mol;
 +            sys->nmolblock++;
 +        }
 +    }
 +    if (sys->nmolblock == 0)
 +    {
 +        gmx_fatal(FARGS, "No molecules were defined in the system");
 +    }
 +
 +    renumber_moltypes(sys, &nrmols, &molinfo);
 +
 +    if (bMorse)
 +    {
 +        convert_harmonics(nrmols, molinfo, atype);
 +    }
 +
 +    if (ir->eDisre == edrNone)
 +    {
 +        i = rm_interactions(F_DISRES, nrmols, molinfo);
 +        if (i > 0)
 +        {
 +            set_warning_line(wi, "unknown", -1);
 +            sprintf(warn_buf, "disre = no, removed %d distance restraints", i);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +    if (opts->bOrire == FALSE)
 +    {
 +        i = rm_interactions(F_ORIRES, nrmols, molinfo);
 +        if (i > 0)
 +        {
 +            set_warning_line(wi, "unknown", -1);
 +            sprintf(warn_buf, "orire = no, removed %d orientation restraints", i);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    /* Copy structures from msys to sys */
 +    molinfo2mtop(nrmols, molinfo, sys);
 +
 +    gmx_mtop_finalize(sys);
 +
 +    /* COORDINATE file processing */
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing coordinates...\n");
 +    }
 +
 +    get_stx_coordnum(confin, &state->natoms);
 +    if (state->natoms != sys->natoms)
 +    {
 +        gmx_fatal(FARGS, "number of coordinates in coordinate file (%s, %d)\n"
 +                  "             does not match topology (%s, %d)",
 +                  confin, state->natoms, topfile, sys->natoms);
 +    }
 +    else
 +    {
 +        /* make space for coordinates and velocities */
 +        char title[STRLEN];
 +        snew(confat, 1);
 +        init_t_atoms(confat, state->natoms, FALSE);
 +        init_state(state, state->natoms, 0, 0, 0, 0);
 +        read_stx_conf(confin, title, confat, state->x, state->v, NULL, state->box);
 +        /* This call fixes the box shape for runs with pressure scaling */
 +        set_box_rel(ir, state);
 +
 +        nmismatch = check_atom_names(topfile, confin, sys, confat);
 +        free_t_atoms(confat, TRUE);
 +        sfree(confat);
 +
 +        if (nmismatch)
 +        {
 +            sprintf(buf, "%d non-matching atom name%s\n"
 +                    "atom names from %s will be used\n"
 +                    "atom names from %s will be ignored\n",
 +                    nmismatch, (nmismatch == 1) ? "" : "s", topfile, confin);
 +            warning(wi, buf);
 +        }
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "double-checking input for internal consistency...\n");
 +        }
 +        double_check(ir, state->box, nint_ftype(sys, molinfo, F_CONSTR), wi);
 +    }
 +
 +    if (bGenVel)
 +    {
 +        real                   *mass;
 +        gmx_mtop_atomloop_all_t aloop;
 +        t_atom                 *atom;
 +        unsigned int            useed;
 +
 +        snew(mass, state->natoms);
 +        aloop = gmx_mtop_atomloop_all_init(sys);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            mass[i] = atom->m;
 +        }
 +
 +        useed = opts->seed;
 +        if (opts->seed == -1)
 +        {
 +            useed = (int)gmx_rng_make_seed();
 +            fprintf(stderr, "Setting gen_seed to %u\n", useed);
 +        }
 +        maxwell_speed(opts->tempi, useed, sys, state->v);
 +
 +        stop_cm(stdout, state->natoms, mass, state->x, state->v);
 +        sfree(mass);
 +    }
 +
 +    *nmi = nrmols;
 +    *mi  = molinfo;
 +}
 +
 +static void copy_state(const char *slog, t_trxframe *fr,
 +                       gmx_bool bReadVel, t_state *state,
 +                       double *use_time)
 +{
 +    int i;
 +
 +    if (fr->not_ok & FRAME_NOT_OK)
 +    {
 +        gmx_fatal(FARGS, "Can not start from an incomplete frame");
 +    }
 +    if (!fr->bX)
 +    {
 +        gmx_fatal(FARGS, "Did not find a frame with coordinates in file %s",
 +                  slog);
 +    }
 +
 +    for (i = 0; i < state->natoms; i++)
 +    {
 +        copy_rvec(fr->x[i], state->x[i]);
 +    }
 +    if (bReadVel)
 +    {
 +        if (!fr->bV)
 +        {
 +            gmx_incons("Trajecory frame unexpectedly does not contain velocities");
 +        }
 +        for (i = 0; i < state->natoms; i++)
 +        {
 +            copy_rvec(fr->v[i], state->v[i]);
 +        }
 +    }
 +    if (fr->bBox)
 +    {
 +        copy_mat(fr->box, state->box);
 +    }
 +
 +    *use_time = fr->time;
 +}
 +
 +static void cont_status(const char *slog, const char *ener,
 +                        gmx_bool bNeedVel, gmx_bool bGenVel, real fr_time,
 +                        t_inputrec *ir, t_state *state,
 +                        gmx_mtop_t *sys,
 +                        const output_env_t oenv)
 +/* If fr_time == -1 read the last frame available which is complete */
 +{
 +    gmx_bool     bReadVel;
 +    t_trxframe   fr;
 +    t_trxstatus *fp;
 +    int          i;
 +    double       use_time;
 +
 +    bReadVel = (bNeedVel && !bGenVel);
 +
 +    fprintf(stderr,
 +            "Reading Coordinates%s and Box size from old trajectory\n",
 +            bReadVel ? ", Velocities" : "");
 +    if (fr_time == -1)
 +    {
 +        fprintf(stderr, "Will read whole trajectory\n");
 +    }
 +    else
 +    {
 +        fprintf(stderr, "Will read till time %g\n", fr_time);
 +    }
 +    if (!bReadVel)
 +    {
 +        if (bGenVel)
 +        {
 +            fprintf(stderr, "Velocities generated: "
 +                    "ignoring velocities in input trajectory\n");
 +        }
 +        read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
 +    }
 +    else
 +    {
 +        read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X | TRX_NEED_V);
 +
 +        if (!fr.bV)
 +        {
 +            fprintf(stderr,
 +                    "\n"
 +                    "WARNING: Did not find a frame with velocities in file %s,\n"
 +                    "         all velocities will be set to zero!\n\n", slog);
 +            for (i = 0; i < sys->natoms; i++)
 +            {
 +                clear_rvec(state->v[i]);
 +            }
 +            close_trj(fp);
 +            /* Search for a frame without velocities */
 +            bReadVel = FALSE;
 +            read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
 +        }
 +    }
 +
 +    state->natoms = fr.natoms;
 +
 +    if (sys->natoms != state->natoms)
 +    {
 +        gmx_fatal(FARGS, "Number of atoms in Topology "
 +                  "is not the same as in Trajectory");
 +    }
 +    copy_state(slog, &fr, bReadVel, state, &use_time);
 +
 +    /* Find the appropriate frame */
 +    while ((fr_time == -1 || fr.time < fr_time) &&
 +           read_next_frame(oenv, fp, &fr))
 +    {
 +        copy_state(slog, &fr, bReadVel, state, &use_time);
 +    }
 +
 +    close_trj(fp);
 +
 +    /* Set the relative box lengths for preserving the box shape.
 +     * Note that this call can lead to differences in the last bit
 +     * with respect to using gmx convert-tpr to create a [TT].tpx[tt] file.
 +     */
 +    set_box_rel(ir, state);
 +
 +    fprintf(stderr, "Using frame at t = %g ps\n", use_time);
 +    fprintf(stderr, "Starting time for run is %g ps\n", ir->init_t);
 +
 +    if ((ir->epc != epcNO  || ir->etc == etcNOSEHOOVER) && ener)
 +    {
 +        get_enx_state(ener, use_time, &sys->groups, ir, state);
 +        preserve_box_shape(ir, state->box_rel, state->boxv);
 +    }
 +}
 +
 +static void read_posres(gmx_mtop_t *mtop, t_molinfo *molinfo, gmx_bool bTopB,
 +                        char *fn,
 +                        int rc_scaling, int ePBC,
 +                        rvec com,
 +                        warninp_t wi)
 +{
 +    gmx_bool        bFirst = TRUE, *hadAtom;
 +    rvec           *x, *v, *xp;
 +    dvec            sum;
 +    double          totmass;
 +    t_atoms         dumat;
 +    matrix          box, invbox;
 +    int             natoms, npbcdim = 0;
 +    char            warn_buf[STRLEN], title[STRLEN];
 +    int             a, i, ai, j, k, mb, nat_molb;
 +    gmx_molblock_t *molb;
 +    t_params       *pr, *prfb;
 +    t_atom         *atom;
 +
 +    get_stx_coordnum(fn, &natoms);
 +    if (natoms != mtop->natoms)
 +    {
 +        sprintf(warn_buf, "The number of atoms in %s (%d) does not match the number of atoms in the topology (%d). Will assume that the first %d atoms in the topology and %s match.", fn, natoms, mtop->natoms, min(mtop->natoms, natoms), fn);
 +        warning(wi, warn_buf);
 +    }
 +    snew(x, natoms);
 +    snew(v, natoms);
 +    init_t_atoms(&dumat, natoms, FALSE);
 +    read_stx_conf(fn, title, &dumat, x, v, NULL, box);
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +    clear_rvec(com);
 +    if (rc_scaling != erscNO)
 +    {
 +        copy_mat(box, invbox);
 +        for (j = npbcdim; j < DIM; j++)
 +        {
 +            clear_rvec(invbox[j]);
 +            invbox[j][j] = 1;
 +        }
 +        m_inv_ur0(invbox, invbox);
 +    }
 +
 +    /* Copy the reference coordinates to mtop */
 +    clear_dvec(sum);
 +    totmass = 0;
 +    a       = 0;
 +    snew(hadAtom, natoms);
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb     = &mtop->molblock[mb];
 +        nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr;
 +        pr       = &(molinfo[molb->type].plist[F_POSRES]);
 +        prfb     = &(molinfo[molb->type].plist[F_FBPOSRES]);
 +        if (pr->nr > 0 || prfb->nr > 0)
 +        {
 +            atom = mtop->moltype[molb->type].atoms.atom;
 +            for (i = 0; (i < pr->nr); i++)
 +            {
 +                ai = pr->param[i].AI;
 +                if (ai >= natoms)
 +                {
 +                    gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n",
 +                              ai+1, *molinfo[molb->type].name, fn, natoms);
 +                }
 +                hadAtom[ai] = TRUE;
 +                if (rc_scaling == erscCOM)
 +                {
 +                    /* Determine the center of mass of the posres reference coordinates */
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        sum[j] += atom[ai].m*x[a+ai][j];
 +                    }
 +                    totmass  += atom[ai].m;
 +                }
 +            }
 +            /* Same for flat-bottomed posres, but do not count an atom twice for COM */
 +            for (i = 0; (i < prfb->nr); i++)
 +            {
 +                ai = prfb->param[i].AI;
 +                if (ai >= natoms)
 +                {
 +                    gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n",
 +                              ai+1, *molinfo[molb->type].name, fn, natoms);
 +                }
 +                if (rc_scaling == erscCOM && hadAtom[ai] == FALSE)
 +                {
 +                    /* Determine the center of mass of the posres reference coordinates */
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        sum[j] += atom[ai].m*x[a+ai][j];
 +                    }
 +                    totmass  += atom[ai].m;
 +                }
 +            }
 +            if (!bTopB)
 +            {
 +                molb->nposres_xA = nat_molb;
 +                snew(molb->posres_xA, molb->nposres_xA);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    copy_rvec(x[a+i], molb->posres_xA[i]);
 +                }
 +            }
 +            else
 +            {
 +                molb->nposres_xB = nat_molb;
 +                snew(molb->posres_xB, molb->nposres_xB);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    copy_rvec(x[a+i], molb->posres_xB[i]);
 +                }
 +            }
 +        }
 +        a += nat_molb;
 +    }
 +    if (rc_scaling == erscCOM)
 +    {
 +        if (totmass == 0)
 +        {
 +            gmx_fatal(FARGS, "The total mass of the position restraint atoms is 0");
 +        }
 +        for (j = 0; j < npbcdim; j++)
 +        {
 +            com[j] = sum[j]/totmass;
 +        }
 +        fprintf(stderr, "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f\n", com[XX], com[YY], com[ZZ]);
 +    }
 +
 +    if (rc_scaling != erscNO)
 +    {
 +        assert(npbcdim <= DIM);
 +
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            molb     = &mtop->molblock[mb];
 +            nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr;
 +            if (molb->nposres_xA > 0 || molb->nposres_xB > 0)
 +            {
 +                xp = (!bTopB ? molb->posres_xA : molb->posres_xB);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        if (rc_scaling == erscALL)
 +                        {
 +                            /* Convert from Cartesian to crystal coordinates */
 +                            xp[i][j] *= invbox[j][j];
 +                            for (k = j+1; k < npbcdim; k++)
 +                            {
 +                                xp[i][j] += invbox[k][j]*xp[i][k];
 +                            }
 +                        }
 +                        else if (rc_scaling == erscCOM)
 +                        {
 +                            /* Subtract the center of mass */
 +                            xp[i][j] -= com[j];
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (rc_scaling == erscCOM)
 +        {
 +            /* Convert the COM from Cartesian to crystal coordinates */
 +            for (j = 0; j < npbcdim; j++)
 +            {
 +                com[j] *= invbox[j][j];
 +                for (k = j+1; k < npbcdim; k++)
 +                {
 +                    com[j] += invbox[k][j]*com[k];
 +                }
 +            }
 +        }
 +    }
 +
 +    free_t_atoms(&dumat, TRUE);
 +    sfree(x);
 +    sfree(v);
 +    sfree(hadAtom);
 +}
 +
 +static void gen_posres(gmx_mtop_t *mtop, t_molinfo *mi,
 +                       char *fnA, char *fnB,
 +                       int rc_scaling, int ePBC,
 +                       rvec com, rvec comB,
 +                       warninp_t wi)
 +{
 +    int i, j;
 +
 +    read_posres  (mtop, mi, FALSE, fnA, rc_scaling, ePBC, com, wi);
 +    if (strcmp(fnA, fnB) != 0)
 +    {
 +        read_posres(mtop, mi, TRUE, fnB, rc_scaling, ePBC, comB, wi);
 +    }
 +}
 +
 +static void set_wall_atomtype(gpp_atomtype_t at, t_gromppopts *opts,
 +                              t_inputrec *ir, warninp_t wi)
 +{
 +    int  i;
 +    char warn_buf[STRLEN];
 +
 +    if (ir->nwall > 0)
 +    {
 +        fprintf(stderr, "Searching the wall atom type(s)\n");
 +    }
 +    for (i = 0; i < ir->nwall; i++)
 +    {
 +        ir->wall_atomtype[i] = get_atomtype_type(opts->wall_atomtype[i], at);
 +        if (ir->wall_atomtype[i] == NOTSET)
 +        {
 +            sprintf(warn_buf, "Specified wall atom type %s is not defined", opts->wall_atomtype[i]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static int nrdf_internal(t_atoms *atoms)
 +{
 +    int i, nmass, nrdf;
 +
 +    nmass = 0;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        /* Vsite ptype might not be set here yet, so also check the mass */
 +        if ((atoms->atom[i].ptype == eptAtom ||
 +             atoms->atom[i].ptype == eptNucleus)
 +            && atoms->atom[i].m > 0)
 +        {
 +            nmass++;
 +        }
 +    }
 +    switch (nmass)
 +    {
 +        case 0:  nrdf = 0; break;
 +        case 1:  nrdf = 0; break;
 +        case 2:  nrdf = 1; break;
 +        default: nrdf = nmass*3 - 6; break;
 +    }
 +
 +    return nrdf;
 +}
 +
 +void
 +spline1d( double        dx,
 +          double *      y,
 +          int           n,
 +          double *      u,
 +          double *      y2 )
 +{
 +    int    i;
 +    double p, q;
 +
 +    y2[0] = 0.0;
 +    u[0]  = 0.0;
 +
 +    for (i = 1; i < n-1; i++)
 +    {
 +        p     = 0.5*y2[i-1]+2.0;
 +        y2[i] = -0.5/p;
 +        q     = (y[i+1]-2.0*y[i]+y[i-1])/dx;
 +        u[i]  = (3.0*q/dx-0.5*u[i-1])/p;
 +    }
 +
 +    y2[n-1] = 0.0;
 +
 +    for (i = n-2; i >= 0; i--)
 +    {
 +        y2[i] = y2[i]*y2[i+1]+u[i];
 +    }
 +}
 +
 +
 +void
 +interpolate1d( double     xmin,
 +               double     dx,
 +               double *   ya,
 +               double *   y2a,
 +               double     x,
 +               double *   y,
 +               double *   y1)
 +{
 +    int    ix;
 +    double a, b;
 +
 +    ix = (x-xmin)/dx;
 +
 +    a = (xmin+(ix+1)*dx-x)/dx;
 +    b = (x-xmin-ix*dx)/dx;
 +
 +    *y  = a*ya[ix]+b*ya[ix+1]+((a*a*a-a)*y2a[ix]+(b*b*b-b)*y2a[ix+1])*(dx*dx)/6.0;
 +    *y1 = (ya[ix+1]-ya[ix])/dx-(3.0*a*a-1.0)/6.0*dx*y2a[ix]+(3.0*b*b-1.0)/6.0*dx*y2a[ix+1];
 +}
 +
 +
 +void
 +setup_cmap (int              grid_spacing,
 +            int              nc,
 +            real *           grid,
 +            gmx_cmap_t *     cmap_grid)
 +{
 +    double *tmp_u, *tmp_u2, *tmp_yy, *tmp_y1, *tmp_t2, *tmp_grid;
 +
 +    int     i, j, k, ii, jj, kk, idx;
 +    int     offset;
 +    double  dx, xmin, v, v1, v2, v12;
 +    double  phi, psi;
 +
 +    snew(tmp_u, 2*grid_spacing);
 +    snew(tmp_u2, 2*grid_spacing);
 +    snew(tmp_yy, 2*grid_spacing);
 +    snew(tmp_y1, 2*grid_spacing);
 +    snew(tmp_t2, 2*grid_spacing*2*grid_spacing);
 +    snew(tmp_grid, 2*grid_spacing*2*grid_spacing);
 +
 +    dx   = 360.0/grid_spacing;
 +    xmin = -180.0-dx*grid_spacing/2;
 +
 +    for (kk = 0; kk < nc; kk++)
 +    {
 +        /* Compute an offset depending on which cmap we are using
 +         * Offset will be the map number multiplied with the
 +         * grid_spacing * grid_spacing * 2
 +         */
 +        offset = kk * grid_spacing * grid_spacing * 2;
 +
 +        for (i = 0; i < 2*grid_spacing; i++)
 +        {
 +            ii = (i+grid_spacing-grid_spacing/2)%grid_spacing;
 +
 +            for (j = 0; j < 2*grid_spacing; j++)
 +            {
 +                jj = (j+grid_spacing-grid_spacing/2)%grid_spacing;
 +                tmp_grid[i*grid_spacing*2+j] = grid[offset+ii*grid_spacing+jj];
 +            }
 +        }
 +
 +        for (i = 0; i < 2*grid_spacing; i++)
 +        {
 +            spline1d(dx, &(tmp_grid[2*grid_spacing*i]), 2*grid_spacing, tmp_u, &(tmp_t2[2*grid_spacing*i]));
 +        }
 +
 +        for (i = grid_spacing/2; i < grid_spacing+grid_spacing/2; i++)
 +        {
 +            ii  = i-grid_spacing/2;
 +            phi = ii*dx-180.0;
 +
 +            for (j = grid_spacing/2; j < grid_spacing+grid_spacing/2; j++)
 +            {
 +                jj  = j-grid_spacing/2;
 +                psi = jj*dx-180.0;
 +
 +                for (k = 0; k < 2*grid_spacing; k++)
 +                {
 +                    interpolate1d(xmin, dx, &(tmp_grid[2*grid_spacing*k]),
 +                                  &(tmp_t2[2*grid_spacing*k]), psi, &tmp_yy[k], &tmp_y1[k]);
 +                }
 +
 +                spline1d(dx, tmp_yy, 2*grid_spacing, tmp_u, tmp_u2);
 +                interpolate1d(xmin, dx, tmp_yy, tmp_u2, phi, &v, &v1);
 +                spline1d(dx, tmp_y1, 2*grid_spacing, tmp_u, tmp_u2);
 +                interpolate1d(xmin, dx, tmp_y1, tmp_u2, phi, &v2, &v12);
 +
 +                idx = ii*grid_spacing+jj;
 +                cmap_grid->cmapdata[kk].cmap[idx*4]   = grid[offset+ii*grid_spacing+jj];
 +                cmap_grid->cmapdata[kk].cmap[idx*4+1] = v1;
 +                cmap_grid->cmapdata[kk].cmap[idx*4+2] = v2;
 +                cmap_grid->cmapdata[kk].cmap[idx*4+3] = v12;
 +            }
 +        }
 +    }
 +}
 +
 +void init_cmap_grid(gmx_cmap_t *cmap_grid, int ngrid, int grid_spacing)
 +{
 +    int i, k, nelem;
 +
 +    cmap_grid->ngrid        = ngrid;
 +    cmap_grid->grid_spacing = grid_spacing;
 +    nelem                   = cmap_grid->grid_spacing*cmap_grid->grid_spacing;
 +
 +    snew(cmap_grid->cmapdata, ngrid);
 +
 +    for (i = 0; i < cmap_grid->ngrid; i++)
 +    {
 +        snew(cmap_grid->cmapdata[i].cmap, 4*nelem);
 +    }
 +}
 +
 +
 +static int count_constraints(gmx_mtop_t *mtop, t_molinfo *mi, warninp_t wi)
 +{
 +    int             count, count_mol, i, mb;
 +    gmx_molblock_t *molb;
 +    t_params       *plist;
 +    char            buf[STRLEN];
 +
 +    count = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        count_mol = 0;
 +        molb      = &mtop->molblock[mb];
 +        plist     = mi[molb->type].plist;
 +
 +        for (i = 0; i < F_NRE; i++)
 +        {
 +            if (i == F_SETTLE)
 +            {
 +                count_mol += 3*plist[i].nr;
 +            }
 +            else if (interaction_function[i].flags & IF_CONSTRAINT)
 +            {
 +                count_mol += plist[i].nr;
 +            }
 +        }
 +
 +        if (count_mol > nrdf_internal(&mi[molb->type].atoms))
 +        {
 +            sprintf(buf,
 +                    "Molecule type '%s' has %d constraints.\n"
 +                    "For stability and efficiency there should not be more constraints than internal number of degrees of freedom: %d.\n",
 +                    *mi[molb->type].name, count_mol,
 +                    nrdf_internal(&mi[molb->type].atoms));
 +            warning(wi, buf);
 +        }
 +        count += molb->nmol*count_mol;
 +    }
 +
 +    return count;
 +}
 +
 +static void check_gbsa_params_charged(gmx_mtop_t *sys, gpp_atomtype_t atype)
 +{
 +    int            i, nmiss, natoms, mt;
 +    real           q;
 +    const t_atoms *atoms;
 +
 +    nmiss = 0;
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        atoms  = &sys->moltype[mt].atoms;
 +        natoms = atoms->nr;
 +
 +        for (i = 0; i < natoms; i++)
 +        {
 +            q = atoms->atom[i].q;
 +            if ((get_atomtype_radius(atoms->atom[i].type, atype)    == 0  ||
 +                 get_atomtype_vol(atoms->atom[i].type, atype)       == 0  ||
 +                 get_atomtype_surftens(atoms->atom[i].type, atype)  == 0  ||
 +                 get_atomtype_gb_radius(atoms->atom[i].type, atype) == 0  ||
 +                 get_atomtype_S_hct(atoms->atom[i].type, atype)     == 0) &&
 +                q != 0)
 +            {
 +                fprintf(stderr, "\nGB parameter(s) zero for atom type '%s' while charge is %g\n",
 +                        get_atomtype_name(atoms->atom[i].type, atype), q);
 +                nmiss++;
 +            }
 +        }
 +    }
 +
 +    if (nmiss > 0)
 +    {
 +        gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield has parameters with value zero for %d atomtypes that occur as charged atoms.", nmiss);
 +    }
 +}
 +
 +
 +static void check_gbsa_params(gpp_atomtype_t atype)
 +{
 +    int  nmiss, i;
 +
 +    /* If we are doing GBSA, check that we got the parameters we need
 +     * This checking is to see if there are GBSA paratmeters for all
 +     * atoms in the force field. To go around this for testing purposes
 +     * comment out the nerror++ counter temporarily
 +     */
 +    nmiss = 0;
 +    for (i = 0; i < get_atomtype_ntypes(atype); i++)
 +    {
 +        if (get_atomtype_radius(i, atype)    < 0 ||
 +            get_atomtype_vol(i, atype)       < 0 ||
 +            get_atomtype_surftens(i, atype)  < 0 ||
 +            get_atomtype_gb_radius(i, atype) < 0 ||
 +            get_atomtype_S_hct(i, atype)     < 0)
 +        {
 +            fprintf(stderr, "\nGB parameter(s) missing or negative for atom type '%s'\n",
 +                    get_atomtype_name(i, atype));
 +            nmiss++;
 +        }
 +    }
 +
 +    if (nmiss > 0)
 +    {
 +        gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield is missing parameters for %d atomtypes or they might be negative.", nmiss);
 +    }
 +
 +}
 +
 +static real calc_temp(const gmx_mtop_t *mtop,
 +                      const t_inputrec *ir,
 +                      rvec             *v)
 +{
 +    double                  sum_mv2;
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     a;
 +    int                     nrdf, g;
 +
 +    sum_mv2 = 0;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        sum_mv2 += atom->m*norm2(v[a]);
 +    }
 +
 +    nrdf = 0;
 +    for (g = 0; g < ir->opts.ngtc; g++)
 +    {
 +        nrdf += ir->opts.nrdf[g];
 +    }
 +
 +    return sum_mv2/(nrdf*BOLTZ);
 +}
 +
 +static real get_max_reference_temp(const t_inputrec *ir,
 +                                   warninp_t         wi)
 +{
 +    real     ref_t;
 +    int      i;
 +    gmx_bool bNoCoupl;
 +
 +    ref_t    = 0;
 +    bNoCoupl = FALSE;
 +    for (i = 0; i < ir->opts.ngtc; i++)
 +    {
 +        if (ir->opts.tau_t[i] < 0)
 +        {
 +            bNoCoupl = TRUE;
 +        }
 +        else
 +        {
 +            ref_t = max(ref_t, ir->opts.ref_t[i]);
 +        }
 +    }
 +
 +    if (bNoCoupl)
 +    {
 +        char buf[STRLEN];
 +
 +        sprintf(buf, "Some temperature coupling groups do not use temperature coupling. We will assume their temperature is not more than %.3f K. If their temperature is higher, the energy error and the Verlet buffer might be underestimated.",
 +                ref_t);
 +        warning(wi, buf);
 +    }
 +
 +    return ref_t;
 +}
 +
 +static void set_verlet_buffer(const gmx_mtop_t *mtop,
 +                              t_inputrec       *ir,
 +                              real              buffer_temp,
 +                              matrix            box,
 +                              warninp_t         wi)
 +{
 +    int                    i;
 +    verletbuf_list_setup_t ls;
 +    real                   rlist_1x1;
 +    int                    n_nonlin_vsite;
 +    char                   warn_buf[STRLEN];
 +
 +    printf("Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K\n", ir->verletbuf_tol, buffer_temp);
 +
 +    /* Calculate the buffer size for simple atom vs atoms list */
 +    ls.cluster_size_i = 1;
 +    ls.cluster_size_j = 1;
 +    calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp,
 +                            &ls, &n_nonlin_vsite, &rlist_1x1);
 +
 +    /* Set the pair-list buffer size in ir */
 +    verletbuf_get_list_setup(FALSE, &ls);
 +    calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp,
 +                            &ls, &n_nonlin_vsite, &ir->rlist);
 +
 +    if (n_nonlin_vsite > 0)
 +    {
 +        sprintf(warn_buf, "There are %d non-linear virtual site constructions. Their contribution to the energy error is approximated. In most cases this does not affect the error significantly.", n_nonlin_vsite);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    printf("Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm\n",
 +           1, 1, rlist_1x1, rlist_1x1-max(ir->rvdw, ir->rcoulomb));
 +
 +    ir->rlistlong = ir->rlist;
 +    printf("Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm\n",
 +           ls.cluster_size_i, ls.cluster_size_j,
 +           ir->rlist, ir->rlist-max(ir->rvdw, ir->rcoulomb));
 +
 +    if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box))
 +    {
 +        gmx_fatal(FARGS, "The pair-list cut-off (%g nm) is longer than half the shortest box vector or longer than the smallest box diagonal element (%g nm). Increase the box size or decrease nstlist or increase verlet-buffer-tolerance.", ir->rlistlong, sqrt(max_cutoff2(ir->ePBC, box)));
 +    }
 +}
 +
 +int gmx_grompp(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[THISMODULE] (the gromacs preprocessor)",
 +        "reads a molecular topology file, checks the validity of the",
 +        "file, expands the topology from a molecular description to an atomic",
 +        "description. The topology file contains information about",
 +        "molecule types and the number of molecules, the preprocessor",
 +        "copies each molecule as needed. ",
 +        "There is no limitation on the number of molecule types. ",
 +        "Bonds and bond-angles can be converted into constraints, separately",
 +        "for hydrogens and heavy atoms.",
 +        "Then a coordinate file is read and velocities can be generated",
 +        "from a Maxwellian distribution if requested.",
 +        "[THISMODULE] also reads parameters for [gmx-mdrun] ",
 +        "(eg. number of MD steps, time step, cut-off), and others such as",
 +        "NEMD parameters, which are corrected so that the net acceleration",
 +        "is zero.",
 +        "Eventually a binary file is produced that can serve as the sole input",
 +        "file for the MD program.[PAR]",
 +
 +        "[THISMODULE] uses the atom names from the topology file. The atom names",
 +        "in the coordinate file (option [TT]-c[tt]) are only read to generate",
 +        "warnings when they do not match the atom names in the topology.",
 +        "Note that the atom names are irrelevant for the simulation as",
 +        "only the atom types are used for generating interaction parameters.[PAR]",
 +
 +        "[THISMODULE] uses a built-in preprocessor to resolve includes, macros, ",
 +        "etc. The preprocessor supports the following keywords:[PAR]",
 +        "#ifdef VARIABLE[BR]",
 +        "#ifndef VARIABLE[BR]",
 +        "#else[BR]",
 +        "#endif[BR]",
 +        "#define VARIABLE[BR]",
 +        "#undef VARIABLE[BR]"
 +        "#include \"filename\"[BR]",
 +        "#include <filename>[PAR]",
 +        "The functioning of these statements in your topology may be modulated by",
 +        "using the following two flags in your [TT].mdp[tt] file:[PAR]",
 +        "[TT]define = -DVARIABLE1 -DVARIABLE2[BR]",
 +        "include = -I/home/john/doe[tt][BR]",
 +        "For further information a C-programming textbook may help you out.",
 +        "Specifying the [TT]-pp[tt] flag will get the pre-processed",
 +        "topology file written out so that you can verify its contents.[PAR]",
 +
 +        /* cpp has been unnecessary for some time, hasn't it?
 +            "If your system does not have a C-preprocessor, you can still",
 +            "use [TT]grompp[tt], but you do not have access to the features ",
 +            "from the cpp. Command line options to the C-preprocessor can be given",
 +            "in the [TT].mdp[tt] file. See your local manual (man cpp).[PAR]",
 +         */
 +
 +        "When using position restraints a file with restraint coordinates",
 +        "can be supplied with [TT]-r[tt], otherwise restraining will be done",
 +        "with respect to the conformation from the [TT]-c[tt] option.",
 +        "For free energy calculation the the coordinates for the B topology",
 +        "can be supplied with [TT]-rb[tt], otherwise they will be equal to",
 +        "those of the A topology.[PAR]",
 +
 +        "Starting coordinates can be read from trajectory with [TT]-t[tt].",
 +        "The last frame with coordinates and velocities will be read,",
 +        "unless the [TT]-time[tt] option is used. Only if this information",
 +        "is absent will the coordinates in the [TT]-c[tt] file be used.",
 +        "Note that these velocities will not be used when [TT]gen_vel = yes[tt]",
 +        "in your [TT].mdp[tt] file. An energy file can be supplied with",
 +        "[TT]-e[tt] to read Nose-Hoover and/or Parrinello-Rahman coupling",
 +        "variables.[PAR]",
 +
 +        "[THISMODULE] can be used to restart simulations (preserving",
 +        "continuity) by supplying just a checkpoint file with [TT]-t[tt].",
 +        "However, for simply changing the number of run steps to extend",
 +        "a run, using [gmx-convert-tpr] is more convenient than [THISMODULE].",
 +        "You then supply the old checkpoint file directly to [gmx-mdrun]",
 +        "with [TT]-cpi[tt]. If you wish to change the ensemble or things",
 +        "like output frequency, then supplying the checkpoint file to",
 +        "[THISMODULE] with [TT]-t[tt] along with a new [TT].mdp[tt] file",
 +        "with [TT]-f[tt] is the recommended procedure.[PAR]",
 +
 +        "By default, all bonded interactions which have constant energy due to",
 +        "virtual site constructions will be removed. If this constant energy is",
 +        "not zero, this will result in a shift in the total energy. All bonded",
 +        "interactions can be kept by turning off [TT]-rmvsbds[tt]. Additionally,",
 +        "all constraints for distances which will be constant anyway because",
 +        "of virtual site constructions will be removed. If any constraints remain",
 +        "which involve virtual sites, a fatal error will result.[PAR]"
 +
 +        "To verify your run input file, please take note of all warnings",
 +        "on the screen, and correct where necessary. Do also look at the contents",
 +        "of the [TT]mdout.mdp[tt] file; this contains comment lines, as well as",
 +        "the input that [THISMODULE] has read. If in doubt, you can start [THISMODULE]",
 +        "with the [TT]-debug[tt] option which will give you more information",
 +        "in a file called [TT]grompp.log[tt] (along with real debug info). You",
 +        "can see the contents of the run input file with the [gmx-dump]",
 +        "program. [gmx-check] can be used to compare the contents of two",
 +        "run input files.[PAR]"
 +
 +        "The [TT]-maxwarn[tt] option can be used to override warnings printed",
 +        "by [THISMODULE] that otherwise halt output. In some cases, warnings are",
 +        "harmless, but usually they are not. The user is advised to carefully",
 +        "interpret the output messages before attempting to bypass them with",
 +        "this option."
 +    };
 +    t_gromppopts      *opts;
 +    gmx_mtop_t        *sys;
 +    int                nmi;
 +    t_molinfo         *mi;
 +    gpp_atomtype_t     atype;
 +    t_inputrec        *ir;
 +    int                natoms, nvsite, comb, mt;
 +    t_params          *plist;
 +    t_state            state;
 +    matrix             box;
 +    real               max_spacing, fudgeQQ;
 +    double             reppow;
 +    char               fn[STRLEN], fnB[STRLEN];
 +    const char        *mdparin;
 +    int                ntype;
 +    gmx_bool           bNeedVel, bGenVel;
 +    gmx_bool           have_atomnumber;
 +    int                n12, n13, n14;
 +    t_params          *gb_plist = NULL;
 +    gmx_genborn_t     *born     = NULL;
 +    output_env_t       oenv;
 +    gmx_bool           bVerbose = FALSE;
 +    warninp_t          wi;
 +    char               warn_buf[STRLEN];
 +    unsigned int       useed;
 +    t_atoms            IMDatoms;   /* Atoms to be operated on interactively (IMD) */
 +
 +    t_filenm           fnm[] = {
 +        { efMDP, NULL,  NULL,        ffREAD  },
 +        { efMDP, "-po", "mdout",     ffWRITE },
 +        { efSTX, "-c",  NULL,        ffREAD  },
 +        { efSTX, "-r",  NULL,        ffOPTRD },
 +        { efSTX, "-rb", NULL,        ffOPTRD },
 +        { efNDX, NULL,  NULL,        ffOPTRD },
 +        { efTOP, NULL,  NULL,        ffREAD  },
 +        { efTOP, "-pp", "processed", ffOPTWR },
 +        { efTPX, "-o",  NULL,        ffWRITE },
 +        { efTRN, "-t",  NULL,        ffOPTRD },
 +        { efEDR, "-e",  NULL,        ffOPTRD },
 +        /* This group is needed by the VMD viewer as the start configuration for IMD sessions: */
 +        { efGRO, "-imd", "imdgroup", ffOPTWR },
 +        { efTRN, "-ref", "rotref",   ffOPTRW }
 +    };
 +#define NFILE asize(fnm)
 +
 +    /* Command line options */
 +    static gmx_bool bRenum   = TRUE;
 +    static gmx_bool bRmVSBds = TRUE, bZero = FALSE;
 +    static int      i, maxwarn = 0;
 +    static real     fr_time = -1;
 +    t_pargs         pa[]    = {
 +        { "-v",       FALSE, etBOOL, {&bVerbose},
 +          "Be loud and noisy" },
 +        { "-time",    FALSE, etREAL, {&fr_time},
 +          "Take frame at or first after this time." },
 +        { "-rmvsbds", FALSE, etBOOL, {&bRmVSBds},
 +          "Remove constant bonded interactions with virtual sites" },
 +        { "-maxwarn", FALSE, etINT,  {&maxwarn},
 +          "Number of allowed warnings during input processing. Not for normal use and may generate unstable systems" },
 +        { "-zero",    FALSE, etBOOL, {&bZero},
 +          "Set parameters for bonded interactions without defaults to zero instead of generating an error" },
 +        { "-renum",   FALSE, etBOOL, {&bRenum},
 +          "Renumber atomtypes and minimize number of atomtypes" }
 +    };
 +
 +    /* Initiate some variables */
 +    snew(ir, 1);
 +    snew(opts, 1);
 +    init_ir(ir, opts);
 +
 +    /* Parse the command line */
 +    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
 +                           asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    wi = init_warning(TRUE, maxwarn);
 +
 +    /* PARAMETER file processing */
 +    mdparin = opt2fn("-f", NFILE, fnm);
 +    set_warning_line(wi, mdparin, -1);
 +    get_ir(mdparin, opt2fn("-po", NFILE, fnm), ir, opts, wi);
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "checking input for internal consistency...\n");
 +    }
 +    check_ir(mdparin, ir, opts, wi);
 +
 +    if (ir->ld_seed == -1)
 +    {
 +        ir->ld_seed = (gmx_int64_t)gmx_rng_make_seed();
 +        fprintf(stderr, "Setting the LD random seed to %"GMX_PRId64 "\n", ir->ld_seed);
 +    }
 +
 +    if (ir->expandedvals->lmc_seed == -1)
 +    {
 +        ir->expandedvals->lmc_seed = (int)gmx_rng_make_seed();
 +        fprintf(stderr, "Setting the lambda MC random seed to %d\n", ir->expandedvals->lmc_seed);
 +    }
 +
 +    bNeedVel = EI_STATE_VELOCITY(ir->eI);
 +    bGenVel  = (bNeedVel && opts->bGenVel);
 +    if (bGenVel && ir->bContinuation)
 +    {
 +        sprintf(warn_buf,
 +                "Generating velocities is inconsistent with attempting "
 +                "to continue a previous run. Choose only one of "
 +                "gen-vel = yes and continuation = yes.");
 +        warning_error(wi, warn_buf);
 +    }
 +
 +    snew(plist, F_NRE);
 +    init_plist(plist);
 +    snew(sys, 1);
 +    atype = init_atomtype();
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "Just opened", &sys->symtab);
 +    }
 +
 +    strcpy(fn, ftp2fn(efTOP, NFILE, fnm));
 +    if (!gmx_fexist(fn))
 +    {
 +        gmx_fatal(FARGS, "%s does not exist", fn);
 +    }
 +    new_status(fn, opt2fn_null("-pp", NFILE, fnm), opt2fn("-c", NFILE, fnm),
 +               opts, ir, bZero, bGenVel, bVerbose, &state,
 +               atype, sys, &nmi, &mi, plist, &comb, &reppow, &fudgeQQ,
 +               opts->bMorse,
 +               wi);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After new_status", &sys->symtab);
 +    }
 +
 +    nvsite = 0;
 +    /* set parameters for virtual site construction (not for vsiten) */
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        nvsite +=
 +            set_vsites(bVerbose, &sys->moltype[mt].atoms, atype, mi[mt].plist);
 +    }
 +    /* now throw away all obsolete bonds, angles and dihedrals: */
 +    /* note: constraints are ALWAYS removed */
 +    if (nvsite)
 +    {
 +        for (mt = 0; mt < sys->nmoltype; mt++)
 +        {
 +            clean_vsite_bondeds(mi[mt].plist, sys->moltype[mt].atoms.nr, bRmVSBds);
 +        }
 +    }
 +
++    if (nvsite && ir->eI == eiNM)
++    {
++        gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
++    }
++
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        fprintf(stderr, "Removing all charge groups because cutoff-scheme=%s\n",
 +                ecutscheme_names[ir->cutoff_scheme]);
 +
 +        /* Remove all charge groups */
 +        gmx_mtop_remove_chargegroups(sys);
 +    }
 +
 +    if (count_constraints(sys, mi, wi) && (ir->eConstrAlg == econtSHAKE))
 +    {
 +        if (ir->eI == eiCG || ir->eI == eiLBFGS)
 +        {
 +            sprintf(warn_buf, "Can not do %s with %s, use %s",
 +                    EI(ir->eI), econstr_names[econtSHAKE], econstr_names[econtLINCS]);
 +            warning_error(wi, warn_buf);
 +        }
 +        if (ir->bPeriodicMols)
 +        {
 +            sprintf(warn_buf, "Can not do periodic molecules with %s, use %s",
 +                    econstr_names[econtSHAKE], econstr_names[econtLINCS]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EI_SD (ir->eI) &&  ir->etc != etcNO)
 +    {
 +        warning_note(wi, "Temperature coupling is ignored with SD integrators.");
 +    }
 +
 +    /* If we are doing QM/MM, check that we got the atom numbers */
 +    have_atomnumber = TRUE;
 +    for (i = 0; i < get_atomtype_ntypes(atype); i++)
 +    {
 +        have_atomnumber = have_atomnumber && (get_atomtype_atomnumber(i, atype) >= 0);
 +    }
 +    if (!have_atomnumber && ir->bQMMM)
 +    {
 +        warning_error(wi,
 +                      "\n"
 +                      "It appears as if you are trying to run a QM/MM calculation, but the force\n"
 +                      "field you are using does not contain atom numbers fields. This is an\n"
 +                      "optional field (introduced in Gromacs 3.3) for general runs, but mandatory\n"
 +                      "for QM/MM. The good news is that it is easy to add - put the atom number as\n"
 +                      "an integer just before the mass column in ffXXXnb.itp.\n"
 +                      "NB: United atoms have the same atom numbers as normal ones.\n\n");
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        if ((ir->adress->const_wf > 1) || (ir->adress->const_wf < 0))
 +        {
 +            warning_error(wi, "AdResS contant weighting function should be between 0 and 1\n\n");
 +        }
 +        /** TODO check size of ex+hy width against box size */
 +    }
 +
 +    /* Check for errors in the input now, since they might cause problems
 +     * during processing further down.
 +     */
 +    check_warning_error(wi, FARGS);
 +
 +    if (opt2bSet("-r", NFILE, fnm))
 +    {
 +        sprintf(fn, "%s", opt2fn("-r", NFILE, fnm));
 +    }
 +    else
 +    {
 +        sprintf(fn, "%s", opt2fn("-c", NFILE, fnm));
 +    }
 +    if (opt2bSet("-rb", NFILE, fnm))
 +    {
 +        sprintf(fnB, "%s", opt2fn("-rb", NFILE, fnm));
 +    }
 +    else
 +    {
 +        strcpy(fnB, fn);
 +    }
 +
 +    if (nint_ftype(sys, mi, F_POSRES) > 0 || nint_ftype(sys, mi, F_FBPOSRES) > 0)
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "Reading position restraint coords from %s", fn);
 +            if (strcmp(fn, fnB) == 0)
 +            {
 +                fprintf(stderr, "\n");
 +            }
 +            else
 +            {
 +                fprintf(stderr, " and %s\n", fnB);
 +            }
 +        }
 +        gen_posres(sys, mi, fn, fnB,
 +                   ir->refcoord_scaling, ir->ePBC,
 +                   ir->posres_com, ir->posres_comB,
 +                   wi);
 +    }
 +
 +    /* If we are using CMAP, setup the pre-interpolation grid */
 +    if (plist->ncmap > 0)
 +    {
 +        init_cmap_grid(&sys->ffparams.cmap_grid, plist->nc, plist->grid_spacing);
 +        setup_cmap(plist->grid_spacing, plist->nc, plist->cmap, &sys->ffparams.cmap_grid);
 +    }
 +
 +    set_wall_atomtype(atype, opts, ir, wi);
 +    if (bRenum)
 +    {
 +        renum_atype(plist, sys, ir->wall_atomtype, atype, bVerbose);
 +        ntype = get_atomtype_ntypes(atype);
 +    }
 +
 +    if (ir->implicit_solvent != eisNO)
 +    {
 +        /* Now we have renumbered the atom types, we can check the GBSA params */
 +        check_gbsa_params(atype);
 +
 +        /* Check that all atoms that have charge and/or LJ-parameters also have
 +         * sensible GB-parameters
 +         */
 +        check_gbsa_params_charged(sys, atype);
 +    }
 +
 +    /* PELA: Copy the atomtype data to the topology atomtype list */
 +    copy_atomtype_atomtypes(atype, &(sys->atomtypes));
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After renum_atype", &sys->symtab);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "converting bonded parameters...\n");
 +    }
 +
 +    ntype = get_atomtype_ntypes(atype);
 +    convert_params(ntype, plist, mi, comb, reppow, fudgeQQ, sys);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After convert_params", &sys->symtab);
 +    }
 +
 +    /* set ptype to VSite for virtual sites */
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        set_vsites_ptype(FALSE, &sys->moltype[mt]);
 +    }
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After virtual sites", &sys->symtab);
 +    }
 +    /* Check velocity for virtual sites and shells */
 +    if (bGenVel)
 +    {
 +        check_vel(sys, state.v);
 +    }
 +
 +    /* check masses */
 +    check_mol(sys, wi);
 +
 +    for (i = 0; i < sys->nmoltype; i++)
 +    {
 +        check_cg_sizes(ftp2fn(efTOP, NFILE, fnm), &sys->moltype[i].cgs, wi);
 +    }
 +
 +    if (EI_DYNAMICS(ir->eI) && ir->eI != eiBD)
 +    {
 +        check_bonds_timestep(sys, ir->delta_t, wi);
 +    }
 +
 +    if (EI_ENERGY_MINIMIZATION(ir->eI) && 0 == ir->nsteps)
 +    {
 +        warning_note(wi, "Zero-step energy minimization will alter the coordinates before calculating the energy. If you just want the energy of a single point, try zero-step MD (with unconstrained_start = yes). To do multiple single-point energy evaluations of different configurations of the same topology, use mdrun -rerun.");
 +    }
 +
 +    check_warning_error(wi, FARGS);
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "initialising group options...\n");
 +    }
 +    do_index(mdparin, ftp2fn_null(efNDX, NFILE, fnm),
 +             sys, bVerbose, ir,
 +             bGenVel ? state.v : NULL,
 +             wi);
 +
 +    if (ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0 &&
 +        ir->nstlist > 1)
 +    {
 +        if (EI_DYNAMICS(ir->eI) && inputrec2nboundeddim(ir) == 3)
 +        {
 +            real buffer_temp;
 +
 +            if (EI_MD(ir->eI) && ir->etc == etcNO)
 +            {
 +                if (bGenVel)
 +                {
 +                    buffer_temp = opts->tempi;
 +                }
 +                else
 +                {
 +                    buffer_temp = calc_temp(sys, ir, state.v);
 +                }
 +                if (buffer_temp > 0)
 +                {
 +                    sprintf(warn_buf, "NVE simulation: will use the initial temperature of %.3f K for determining the Verlet buffer size", buffer_temp);
 +                    warning_note(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    sprintf(warn_buf, "NVE simulation with an initial temperature of zero: will use a Verlet buffer of %d%%. Check your energy drift!",
 +                            (int)(verlet_buffer_ratio_NVE_T0*100 + 0.5));
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +            else
 +            {
 +                buffer_temp = get_max_reference_temp(ir, wi);
 +            }
 +
 +            if (EI_MD(ir->eI) && ir->etc == etcNO && buffer_temp == 0)
 +            {
 +                /* NVE with initial T=0: we add a fixed ratio to rlist.
 +                 * Since we don't actually use verletbuf_tol, we set it to -1
 +                 * so it can't be misused later.
 +                 */
 +                ir->rlist         *= 1.0 + verlet_buffer_ratio_NVE_T0;
 +                ir->verletbuf_tol  = -1;
 +            }
 +            else
 +            {
 +                /* We warn for NVE simulations with >1(.1)% drift tolerance */
 +                const real drift_tol = 0.01;
 +                real       ener_runtime;
 +
 +                /* We use 2 DOF per atom = 2kT pot+kin energy, to be on
 +                 * the safe side with constraints (without constraints: 3 DOF).
 +                 */
 +                ener_runtime = 2*BOLTZ*buffer_temp/(ir->nsteps*ir->delta_t);
 +
 +                if (EI_MD(ir->eI) && ir->etc == etcNO && ir->nstlist > 1 &&
 +                    ir->nsteps > 0 &&
 +                    ir->verletbuf_tol > 1.1*drift_tol*ener_runtime)
 +                {
 +                    sprintf(warn_buf, "You are using a Verlet buffer tolerance of %g kJ/mol/ps for an NVE simulation of length %g ps, which can give a final drift of %d%%. For conserving energy to %d%%, you might need to set verlet-buffer-tolerance to %.1e.",
 +                            ir->verletbuf_tol, ir->nsteps*ir->delta_t,
 +                            (int)(ir->verletbuf_tol/ener_runtime*100 + 0.5),
 +                            (int)(100*drift_tol + 0.5),
 +                            drift_tol*ener_runtime);
 +                    warning_note(wi, warn_buf);
 +                }
 +
 +                set_verlet_buffer(sys, ir, buffer_temp, state.box, wi);
 +            }
 +        }
 +    }
 +
 +    /* Init the temperature coupling state */
 +    init_gtc_state(&state, ir->opts.ngtc, 0, ir->opts.nhchainlength); /* need to add nnhpres here? */
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Checking consistency between energy and charge groups...\n");
 +    }
 +    check_eg_vs_cg(sys);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After index", &sys->symtab);
 +    }
 +
 +    triple_check(mdparin, ir, sys, wi);
 +    close_symtab(&sys->symtab);
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After close", &sys->symtab);
 +    }
 +
 +    /* make exclusions between QM atoms */
 +    if (ir->bQMMM)
 +    {
 +        if (ir->QMMMscheme == eQMMMschemenormal && ir->ns_type == ensSIMPLE)
 +        {
 +            gmx_fatal(FARGS, "electrostatic embedding only works with grid neighboursearching, use ns-type=grid instead\n");
 +        }
 +        else
 +        {
 +            generate_qmexcl(sys, ir, wi);
 +        }
 +    }
 +
 +    if (ftp2bSet(efTRN, NFILE, fnm))
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "getting data from old trajectory ...\n");
 +        }
 +        cont_status(ftp2fn(efTRN, NFILE, fnm), ftp2fn_null(efEDR, NFILE, fnm),
 +                    bNeedVel, bGenVel, fr_time, ir, &state, sys, oenv);
 +    }
 +
 +    if (ir->ePBC == epbcXY && ir->nwall != 2)
 +    {
 +        clear_rvec(state.box[ZZ]);
 +    }
 +
 +    if (ir->cutoff_scheme != ecutsVERLET && ir->rlist > 0)
 +    {
 +        set_warning_line(wi, mdparin, -1);
 +        check_chargegroup_radii(sys, ir, state.x, wi);
 +    }
 +
 +    if (EEL_FULL(ir->coulombtype) || EVDW_PME(ir->vdwtype))
 +    {
 +        /* Calculate the optimal grid dimensions */
 +        copy_mat(state.box, box);
 +        if (ir->ePBC == epbcXY && ir->nwall == 2)
 +        {
 +            svmul(ir->wall_ewald_zfac, box[ZZ], box[ZZ]);
 +        }
 +        if (ir->nkx > 0 && ir->nky > 0 && ir->nkz > 0)
 +        {
 +            /* Mark fourier_spacing as not used */
 +            ir->fourier_spacing = 0;
 +        }
 +        else if (ir->nkx != 0 && ir->nky != 0 && ir->nkz != 0)
 +        {
 +            set_warning_line(wi, mdparin, -1);
 +            warning_error(wi, "Some of the Fourier grid sizes are set, but all of them need to be set.");
 +        }
 +        max_spacing = calc_grid(stdout, box, ir->fourier_spacing,
 +                                &(ir->nkx), &(ir->nky), &(ir->nkz));
 +    }
 +
 +    /* MRS: eventually figure out better logic for initializing the fep
 +       values that makes declaring the lambda and declaring the state not
 +       potentially conflict if not handled correctly. */
 +    if (ir->efep != efepNO)
 +    {
 +        state.fep_state = ir->fepvals->init_fep_state;
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            /* init_lambda trumps state definitions*/
 +            if (ir->fepvals->init_lambda >= 0)
 +            {
 +                state.lambda[i] = ir->fepvals->init_lambda;
 +            }
 +            else
 +            {
 +                if (ir->fepvals->all_lambda[i] == NULL)
 +                {
 +                    gmx_fatal(FARGS, "Values of lambda not set for a free energy calculation!");
 +                }
 +                else
 +                {
 +                    state.lambda[i] = ir->fepvals->all_lambda[i][state.fep_state];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        set_pull_init(ir, sys, state.x, state.box, state.lambda[efptMASS], oenv, opts->pull_start);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        set_reference_positions(ir->rot, state.x, state.box,
 +                                opt2fn("-ref", NFILE, fnm), opt2bSet("-ref", NFILE, fnm),
 +                                wi);
 +    }
 +
 +    /*  reset_multinr(sys); */
 +
 +    if (EEL_PME(ir->coulombtype))
 +    {
 +        float ratio = pme_load_estimate(sys, ir, state.box);
 +        fprintf(stderr, "Estimate for the relative computational load of the PME mesh part: %.2f\n", ratio);
 +        /* With free energy we might need to do PME both for the A and B state
 +         * charges. This will double the cost, but the optimal performance will
 +         * then probably be at a slightly larger cut-off and grid spacing.
 +         */
 +        if ((ir->efep == efepNO && ratio > 1.0/2.0) ||
 +            (ir->efep != efepNO && ratio > 2.0/3.0))
 +        {
 +            warning_note(wi,
 +                         "The optimal PME mesh load for parallel simulations is below 0.5\n"
 +                         "and for highly parallel simulations between 0.25 and 0.33,\n"
 +                         "for higher performance, increase the cut-off and the PME grid spacing.\n");
 +            if (ir->efep != efepNO)
 +            {
 +                warning_note(wi,
 +                             "For free energy simulations, the optimal load limit increases from 0.5 to 0.667\n");
 +            }
 +        }
 +    }
 +
 +    {
 +        char   warn_buf[STRLEN];
 +        double cio = compute_io(ir, sys->natoms, &sys->groups, F_NRE, 1);
 +        sprintf(warn_buf, "This run will generate roughly %.0f Mb of data", cio);
 +        if (cio > 2000)
 +        {
 +            set_warning_line(wi, mdparin, -1);
 +            warning_note(wi, warn_buf);
 +        }
 +        else
 +        {
 +            printf("%s\n", warn_buf);
 +        }
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "writing run input file...\n");
 +    }
 +
 +    done_warning(wi, FARGS);
 +    write_tpx_state(ftp2fn(efTPX, NFILE, fnm), ir, &state, sys);
 +
 +    /* Output IMD group, if bIMD is TRUE */
 +    write_IMDgroup_to_file(ir->bIMD, ir, &state, sys, NFILE, fnm);
 +
 +    done_atomtype(atype);
 +    done_mtop(sys, TRUE);
 +    done_inputrec_strings();
 +
 +    return 0;
 +}
index 2d206d01dfa03044cfb03d33e9084c932f186427,0000000000000000000000000000000000000000..68a2a71d73129744caa8b6471698f4515461890a
mode 100644,000000..100644
--- /dev/null
@@@ -1,4390 -1,0 +1,4396 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <ctype.h>
 +#include <stdlib.h>
 +#include <limits.h>
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "typedefs.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "macros.h"
 +#include "index.h"
 +#include "symtab.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "readinp.h"
 +#include "warninp.h"
 +#include "readir.h"
 +#include "toputil.h"
 +#include "index.h"
 +#include "network.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "mtop_util.h"
 +#include "chargegroup.h"
 +#include "inputrec.h"
 +#include "calc_verletbuf.h"
 +
 +#define MAXPTR 254
 +#define NOGID  255
 +
 +/* Resource parameters
 + * Do not change any of these until you read the instruction
 + * in readinp.h. Some cpp's do not take spaces after the backslash
 + * (like the c-shell), which will give you a very weird compiler
 + * message.
 + */
 +
 +typedef struct t_inputrec_strings
 +{
 +    char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN],
 +         acc[STRLEN], accgrps[STRLEN], freeze[STRLEN], frdim[STRLEN],
 +         energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN],
 +         couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN],
 +         wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN],
 +         imd_grp[STRLEN];
 +    char   fep_lambda[efptNR][STRLEN];
 +    char   lambda_weights[STRLEN];
 +    char **pull_grp;
 +    char **rot_grp;
 +    char   anneal[STRLEN], anneal_npoints[STRLEN],
 +           anneal_time[STRLEN], anneal_temp[STRLEN];
 +    char   QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN],
 +           bSH[STRLEN], CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN],
 +           SAoff[STRLEN], SAsteps[STRLEN], bTS[STRLEN], bOPT[STRLEN];
 +    char efield_x[STRLEN], efield_xt[STRLEN], efield_y[STRLEN],
 +         efield_yt[STRLEN], efield_z[STRLEN], efield_zt[STRLEN];
 +
 +} gmx_inputrec_strings;
 +
 +static gmx_inputrec_strings *is = NULL;
 +
 +void init_inputrec_strings()
 +{
 +    if (is)
 +    {
 +        gmx_incons("Attempted to call init_inputrec_strings before calling done_inputrec_strings. Only one inputrec (i.e. .mdp file) can be parsed at a time.");
 +    }
 +    snew(is, 1);
 +}
 +
 +void done_inputrec_strings()
 +{
 +    sfree(is);
 +    is = NULL;
 +}
 +
 +static char swapgrp[STRLEN], splitgrp0[STRLEN], splitgrp1[STRLEN], solgrp[STRLEN];
 +
 +enum {
 +    egrptpALL,         /* All particles have to be a member of a group.     */
 +    egrptpALL_GENREST, /* A rest group with name is generated for particles *
 +                        * that are not part of any group.                   */
 +    egrptpPART,        /* As egrptpALL_GENREST, but no name is generated    *
 +                        * for the rest group.                               */
 +    egrptpONE          /* Merge all selected groups into one group,         *
 +                        * make a rest group for the remaining particles.    */
 +};
 +
 +static const char *constraints[eshNR+1]    = {
 +    "none", "h-bonds", "all-bonds", "h-angles", "all-angles", NULL
 +};
 +
 +static const char *couple_lam[ecouplamNR+1]    = {
 +    "vdw-q", "vdw", "q", "none", NULL
 +};
 +
 +void init_ir(t_inputrec *ir, t_gromppopts *opts)
 +{
 +    snew(opts->include, STRLEN);
 +    snew(opts->define, STRLEN);
 +    snew(ir->fepvals, 1);
 +    snew(ir->expandedvals, 1);
 +    snew(ir->simtempvals, 1);
 +}
 +
 +static void GetSimTemps(int ntemps, t_simtemp *simtemp, double *temperature_lambdas)
 +{
 +
 +    int i;
 +
 +    for (i = 0; i < ntemps; i++)
 +    {
 +        /* simple linear scaling -- allows more control */
 +        if (simtemp->eSimTempScale == esimtempLINEAR)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*temperature_lambdas[i];
 +        }
 +        else if (simtemp->eSimTempScale == esimtempGEOMETRIC)  /* should give roughly equal acceptance for constant heat capacity . . . */
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low * pow(simtemp->simtemp_high/simtemp->simtemp_low, (1.0*i)/(ntemps-1));
 +        }
 +        else if (simtemp->eSimTempScale == esimtempEXPONENTIAL)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*((exp(temperature_lambdas[i])-1)/(exp(1.0)-1));
 +        }
 +        else
 +        {
 +            char errorstr[128];
 +            sprintf(errorstr, "eSimTempScale=%d not defined", simtemp->eSimTempScale);
 +            gmx_fatal(FARGS, errorstr);
 +        }
 +    }
 +}
 +
 +
 +
 +static void _low_check(gmx_bool b, char *s, warninp_t wi)
 +{
 +    if (b)
 +    {
 +        warning_error(wi, s);
 +    }
 +}
 +
 +static void check_nst(const char *desc_nst, int nst,
 +                      const char *desc_p, int *p,
 +                      warninp_t wi)
 +{
 +    char buf[STRLEN];
 +
 +    if (*p > 0 && *p % nst != 0)
 +    {
 +        /* Round up to the next multiple of nst */
 +        *p = ((*p)/nst + 1)*nst;
 +        sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n",
 +                desc_p, desc_nst, desc_p, *p);
 +        warning(wi, buf);
 +    }
 +}
 +
 +static gmx_bool ir_NVE(const t_inputrec *ir)
 +{
 +    return ((ir->eI == eiMD || EI_VV(ir->eI)) && ir->etc == etcNO);
 +}
 +
 +static int lcd(int n1, int n2)
 +{
 +    int d, i;
 +
 +    d = 1;
 +    for (i = 2; (i <= n1 && i <= n2); i++)
 +    {
 +        if (n1 % i == 0 && n2 % i == 0)
 +        {
 +            d = i;
 +        }
 +    }
 +
 +    return d;
 +}
 +
 +static void process_interaction_modifier(const t_inputrec *ir, int *eintmod)
 +{
 +    if (*eintmod == eintmodPOTSHIFT_VERLET)
 +    {
 +        if (ir->cutoff_scheme == ecutsVERLET)
 +        {
 +            *eintmod = eintmodPOTSHIFT;
 +        }
 +        else
 +        {
 +            *eintmod = eintmodNONE;
 +        }
 +    }
 +}
 +
 +void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts,
 +              warninp_t wi)
 +/* Check internal consistency.
 + * NOTE: index groups are not set here yet, don't check things
 + * like temperature coupling group options here, but in triple_check
 + */
 +{
 +    /* Strange macro: first one fills the err_buf, and then one can check
 +     * the condition, which will print the message and increase the error
 +     * counter.
 +     */
 +#define CHECK(b) _low_check(b, err_buf, wi)
 +    char        err_buf[256], warn_buf[STRLEN];
 +    int         i, j;
 +    int         ns_type  = 0;
 +    real        dt_coupl = 0;
 +    real        dt_pcoupl;
 +    int         nstcmin;
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    /* BASIC CUT-OFF STUFF */
 +    if (ir->rcoulomb < 0)
 +    {
 +        warning_error(wi, "rcoulomb should be >= 0");
 +    }
 +    if (ir->rvdw < 0)
 +    {
 +        warning_error(wi, "rvdw should be >= 0");
 +    }
 +    if (ir->rlist < 0 &&
 +        !(ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0))
 +    {
 +        warning_error(wi, "rlist should be >= 0");
 +    }
 +
 +    process_interaction_modifier(ir, &ir->coulomb_modifier);
 +    process_interaction_modifier(ir, &ir->vdw_modifier);
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        warning_note(wi,
 +                     "The group cutoff scheme is deprecated in Gromacs 5.0 and will be removed in a future "
 +                     "release when all interaction forms are supported for the verlet scheme. The verlet "
 +                     "scheme already scales better, and it is compatible with GPUs and other accelerators.");
 +
 +        /* BASIC CUT-OFF STUFF */
 +        if (ir->rlist == 0 ||
 +            !((ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > ir->rlist) ||
 +              (ir_vdw_might_be_zero_at_cutoff(ir)     && ir->rvdw     > ir->rlist)))
 +        {
 +            /* No switched potential and/or no twin-range:
 +             * we can set the long-range cut-off to the maximum of the other cut-offs.
 +             */
 +            ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
 +        }
 +        else if (ir->rlistlong < 0)
 +        {
 +            ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
 +            sprintf(warn_buf, "rlistlong was not set, setting it to %g (no buffer)",
 +                    ir->rlistlong);
 +            warning(wi, warn_buf);
 +        }
 +        if (ir->rlistlong == 0 && ir->ePBC != epbcNONE)
 +        {
 +            warning_error(wi, "Can not have an infinite cut-off with PBC");
 +        }
 +        if (ir->rlistlong > 0 && (ir->rlist == 0 || ir->rlistlong < ir->rlist))
 +        {
 +            warning_error(wi, "rlistlong can not be shorter than rlist");
 +        }
 +        if (IR_TWINRANGE(*ir) && ir->nstlist <= 0)
 +        {
 +            warning_error(wi, "Can not have nstlist<=0 with twin-range interactions");
 +        }
 +    }
 +
 +    if (ir->rlistlong == ir->rlist)
 +    {
 +        ir->nstcalclr = 0;
 +    }
 +    else if (ir->rlistlong > ir->rlist && ir->nstcalclr == 0)
 +    {
 +        warning_error(wi, "With different cutoffs for electrostatics and VdW, nstcalclr must be -1 or a positive number");
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        real rc_max;
 +
 +        /* Normal Verlet type neighbor-list, currently only limited feature support */
 +        if (inputrec2nboundeddim(ir) < 3)
 +        {
 +            warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported");
 +        }
 +        if (ir->rcoulomb != ir->rvdw)
 +        {
 +            warning_error(wi, "With Verlet lists rcoulomb!=rvdw is not supported");
 +        }
 +        if (ir->vdwtype == evdwSHIFT || ir->vdwtype == evdwSWITCH)
 +        {
 +            if (ir->vdw_modifier == eintmodNONE ||
 +                ir->vdw_modifier == eintmodPOTSHIFT)
 +            {
 +                ir->vdw_modifier = (ir->vdwtype == evdwSHIFT ? eintmodFORCESWITCH : eintmodPOTSWITCH);
 +
 +                sprintf(warn_buf, "Replacing vdwtype=%s by the equivalent combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], evdw_names[evdwCUT], eintmod_names[ir->vdw_modifier]);
 +                warning_note(wi, warn_buf);
 +
 +                ir->vdwtype = evdwCUT;
 +            }
 +            else
 +            {
 +                sprintf(warn_buf, "Unsupported combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], eintmod_names[ir->vdw_modifier]);
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +
 +        if (!(ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME))
 +        {
 +            warning_error(wi, "With Verlet lists only cut-off and PME LJ interactions are supported");
 +        }
 +        if (!(ir->coulombtype == eelCUT ||
 +              (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC) ||
 +              EEL_PME(ir->coulombtype) || ir->coulombtype == eelEWALD))
 +        {
 +            warning_error(wi, "With Verlet lists only cut-off, reaction-field, PME and Ewald electrostatics are supported");
 +        }
 +        if (!(ir->coulomb_modifier == eintmodNONE ||
 +              ir->coulomb_modifier == eintmodPOTSHIFT))
 +        {
 +            sprintf(warn_buf, "coulomb_modifier=%s is not supported with the Verlet cut-off scheme", eintmod_names[ir->coulomb_modifier]);
 +            warning_error(wi, warn_buf);
 +        }
 +
 +        if (ir->nstlist <= 0)
 +        {
 +            warning_error(wi, "With Verlet lists nstlist should be larger than 0");
 +        }
 +
 +        if (ir->nstlist < 10)
 +        {
 +            warning_note(wi, "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note that with the Verlet scheme, nstlist has no effect on the accuracy of your simulation.");
 +        }
 +
 +        rc_max = max(ir->rvdw, ir->rcoulomb);
 +
 +        if (ir->verletbuf_tol <= 0)
 +        {
 +            if (ir->verletbuf_tol == 0)
 +            {
 +                warning_error(wi, "Can not have Verlet buffer tolerance of exactly 0");
 +            }
 +
 +            if (ir->rlist < rc_max)
 +            {
 +                warning_error(wi, "With verlet lists rlist can not be smaller than rvdw or rcoulomb");
 +            }
 +
 +            if (ir->rlist == rc_max && ir->nstlist > 1)
 +            {
 +                warning_note(wi, "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet buffer. The cluster pair list does have a buffering effect, but choosing a larger rlist might be necessary for good energy conservation.");
 +            }
 +        }
 +        else
 +        {
 +            if (ir->rlist > rc_max)
 +            {
 +                warning_note(wi, "You have set rlist larger than the interaction cut-off, but you also have verlet-buffer-tolerance > 0. Will set rlist using verlet-buffer-tolerance.");
 +            }
 +
 +            if (ir->nstlist == 1)
 +            {
 +                /* No buffer required */
 +                ir->rlist = rc_max;
 +            }
 +            else
 +            {
 +                if (EI_DYNAMICS(ir->eI))
 +                {
 +                    if (inputrec2nboundeddim(ir) < 3)
 +                    {
 +                        warning_error(wi, "The box volume is required for calculating rlist from the energy drift with verlet-buffer-tolerance > 0. You are using at least one unbounded dimension, so no volume can be computed. Either use a finite box, or set rlist yourself together with verlet-buffer-tolerance = -1.");
 +                    }
 +                    /* Set rlist temporarily so we can continue processing */
 +                    ir->rlist = rc_max;
 +                }
 +                else
 +                {
 +                    /* Set the buffer to 5% of the cut-off */
 +                    ir->rlist = (1.0 + verlet_buffer_ratio_nodynamics)*rc_max;
 +                }
 +            }
 +        }
 +
 +        /* No twin-range calculations with Verlet lists */
 +        ir->rlistlong = ir->rlist;
 +    }
 +
 +    if (ir->nstcalclr == -1)
 +    {
 +        /* if rlist=rlistlong, this will later be changed to nstcalclr=0 */
 +        ir->nstcalclr = ir->nstlist;
 +    }
 +    else if (ir->nstcalclr > 0)
 +    {
 +        if (ir->nstlist > 0 && (ir->nstlist % ir->nstcalclr != 0))
 +        {
 +            warning_error(wi, "nstlist must be evenly divisible by nstcalclr. Use nstcalclr = -1 to automatically follow nstlist");
 +        }
 +    }
 +    else if (ir->nstcalclr < -1)
 +    {
 +        warning_error(wi, "nstcalclr must be a positive number (divisor of nstcalclr), or -1 to follow nstlist.");
 +    }
 +
 +    if (EEL_PME(ir->coulombtype) && ir->rcoulomb > ir->rvdw && ir->nstcalclr > 1)
 +    {
 +        warning_error(wi, "When used with PME, the long-range component of twin-range interactions must be updated every step (nstcalclr)");
 +    }
 +
 +    /* GENERAL INTEGRATOR STUFF */
 +    if (!(ir->eI == eiMD || EI_VV(ir->eI)))
 +    {
 +        ir->etc = etcNO;
 +    }
 +    if (ir->eI == eiVVAK)
 +    {
 +        sprintf(warn_buf, "Integrator method %s is implemented primarily for validation purposes; for molecular dynamics, you should probably be using %s or %s", ei_names[eiVVAK], ei_names[eiMD], ei_names[eiVV]);
 +        warning_note(wi, warn_buf);
 +    }
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        ir->epc = epcNO;
 +    }
 +    if (EI_DYNAMICS(ir->eI))
 +    {
 +        if (ir->nstcalcenergy < 0)
 +        {
 +            ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir);
 +            if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy)
 +            {
 +                /* nstcalcenergy larger than nstener does not make sense.
 +                 * We ideally want nstcalcenergy=nstener.
 +                 */
 +                if (ir->nstlist > 0)
 +                {
 +                    ir->nstcalcenergy = lcd(ir->nstenergy, ir->nstlist);
 +                }
 +                else
 +                {
 +                    ir->nstcalcenergy = ir->nstenergy;
 +                }
 +            }
 +        }
 +        else if ( (ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy) ||
 +                  (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 &&
 +                   (ir->nstcalcenergy > ir->fepvals->nstdhdl) ) )
 +
 +        {
 +            const char *nsten    = "nstenergy";
 +            const char *nstdh    = "nstdhdl";
 +            const char *min_name = nsten;
 +            int         min_nst  = ir->nstenergy;
 +
 +            /* find the smallest of ( nstenergy, nstdhdl ) */
 +            if (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 &&
 +                (ir->nstenergy == 0 || ir->fepvals->nstdhdl < ir->nstenergy))
 +            {
 +                min_nst  = ir->fepvals->nstdhdl;
 +                min_name = nstdh;
 +            }
 +            /* If the user sets nstenergy small, we should respect that */
 +            sprintf(warn_buf,
 +                    "Setting nstcalcenergy (%d) equal to %s (%d)",
 +                    ir->nstcalcenergy, min_name, min_nst);
 +            warning_note(wi, warn_buf);
 +            ir->nstcalcenergy = min_nst;
 +        }
 +
 +        if (ir->epc != epcNO)
 +        {
 +            if (ir->nstpcouple < 0)
 +            {
 +                ir->nstpcouple = ir_optimal_nstpcouple(ir);
 +            }
 +        }
 +        if (IR_TWINRANGE(*ir))
 +        {
 +            check_nst("nstlist", ir->nstlist,
 +                      "nstcalcenergy", &ir->nstcalcenergy, wi);
 +            if (ir->epc != epcNO)
 +            {
 +                check_nst("nstlist", ir->nstlist,
 +                          "nstpcouple", &ir->nstpcouple, wi);
 +            }
 +        }
 +
 +        if (ir->nstcalcenergy > 0)
 +        {
 +            if (ir->efep != efepNO)
 +            {
 +                /* nstdhdl should be a multiple of nstcalcenergy */
 +                check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                          "nstdhdl", &ir->fepvals->nstdhdl, wi);
 +                /* nstexpanded should be a multiple of nstcalcenergy */
 +                check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                          "nstexpanded", &ir->expandedvals->nstexpanded, wi);
 +            }
 +            /* for storing exact averages nstenergy should be
 +             * a multiple of nstcalcenergy
 +             */
 +            check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                      "nstenergy", &ir->nstenergy, wi);
 +        }
 +    }
 +
 +    /* LD STUFF */
 +    if ((EI_SD(ir->eI) || ir->eI == eiBD) &&
 +        ir->bContinuation && ir->ld_seed != -1)
 +    {
 +        warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)");
 +    }
 +
 +    /* TPI STUFF */
 +    if (EI_TPI(ir->eI))
 +    {
 +        sprintf(err_buf, "TPI only works with pbc = %s", epbc_names[epbcXYZ]);
 +        CHECK(ir->ePBC != epbcXYZ);
 +        sprintf(err_buf, "TPI only works with ns = %s", ens_names[ensGRID]);
 +        CHECK(ir->ns_type != ensGRID);
 +        sprintf(err_buf, "with TPI nstlist should be larger than zero");
 +        CHECK(ir->nstlist <= 0);
 +        sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
 +        CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype));
 +    }
 +
 +    /* SHAKE / LINCS */
 +    if ( (opts->nshake > 0) && (opts->bMorse) )
 +    {
 +        sprintf(warn_buf,
 +                "Using morse bond-potentials while constraining bonds is useless");
 +        warning(wi, warn_buf);
 +    }
 +
 +    if ((EI_SD(ir->eI) || ir->eI == eiBD) &&
 +        ir->bContinuation && ir->ld_seed != -1)
 +    {
 +        warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)");
 +    }
 +    /* verify simulated tempering options */
 +
 +    if (ir->bSimTemp)
 +    {
 +        gmx_bool bAllTempZero = TRUE;
 +        for (i = 0; i < fep->n_lambda; i++)
 +        {
 +            sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[efptTEMPERATURE], fep->all_lambda[efptTEMPERATURE][i]);
 +            CHECK((fep->all_lambda[efptTEMPERATURE][i] < 0) || (fep->all_lambda[efptTEMPERATURE][i] > 1));
 +            if (fep->all_lambda[efptTEMPERATURE][i] > 0)
 +            {
 +                bAllTempZero = FALSE;
 +            }
 +        }
 +        sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero");
 +        CHECK(bAllTempZero == TRUE);
 +
 +        sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv");
 +        CHECK(ir->eI != eiVV);
 +
 +        /* check compatability of the temperature coupling with simulated tempering */
 +
 +        if (ir->etc == etcNOSEHOOVER)
 +        {
 +            sprintf(warn_buf, "Nose-Hoover based temperature control such as [%s] my not be entirelyconsistent with simulated tempering", etcoupl_names[ir->etc]);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        /* check that the temperatures make sense */
 +
 +        sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= than the simulated tempering lower temperature (%g)", ir->simtempvals->simtemp_high, ir->simtempvals->simtemp_low);
 +        CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low);
 +
 +        sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_high);
 +        CHECK(ir->simtempvals->simtemp_high <= 0);
 +
 +        sprintf(err_buf, "Lower simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_low);
 +        CHECK(ir->simtempvals->simtemp_low <= 0);
 +    }
 +
 +    /* verify free energy options */
 +
 +    if (ir->efep != efepNO)
 +    {
 +        fep = ir->fepvals;
 +        sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2",
 +                fep->sc_power);
 +        CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2);
 +
 +        sprintf(err_buf, "The soft-core sc-r-power is %d and can only be 6 or 48",
 +                (int)fep->sc_r_power);
 +        CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0 && fep->sc_r_power != 48.0);
 +
 +        sprintf(err_buf, "Can't use postive delta-lambda (%g) if initial state/lambda does not start at zero", fep->delta_lambda);
 +        CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) ||  (fep->init_lambda > 0)));
 +
 +        sprintf(err_buf, "Can't use postive delta-lambda (%g) with expanded ensemble simulations", fep->delta_lambda);
 +        CHECK(fep->delta_lambda > 0 && (ir->efep == efepEXPANDED));
 +
 +        sprintf(err_buf, "Can only use expanded ensemble with md-vv for now; should be supported for other integrators in 5.0");
 +        CHECK(!(EI_VV(ir->eI)) && (ir->efep == efepEXPANDED));
 +
 +        sprintf(err_buf, "Free-energy not implemented for Ewald");
 +        CHECK(ir->coulombtype == eelEWALD);
 +
 +        /* check validty of lambda inputs */
 +        if (fep->n_lambda == 0)
 +        {
 +            /* Clear output in case of no states:*/
 +            sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state);
 +            CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0));
 +        }
 +        else
 +        {
 +            sprintf(err_buf, "initial thermodynamic state %d does not exist, only goes to %d", fep->init_fep_state, fep->n_lambda-1);
 +            CHECK((fep->init_fep_state >= fep->n_lambda));
 +        }
 +
 +        sprintf(err_buf, "Lambda state must be set, either with init-lambda-state or with init-lambda");
 +        CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0));
 +
 +        sprintf(err_buf, "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with init-lambda-state or with init-lambda, but not both",
 +                fep->init_lambda, fep->init_fep_state);
 +        CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0));
 +
 +
 +
 +        if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0))
 +        {
 +            int n_lambda_terms;
 +            n_lambda_terms = 0;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                if (fep->separate_dvdl[i])
 +                {
 +                    n_lambda_terms++;
 +                }
 +            }
 +            if (n_lambda_terms > 1)
 +            {
 +                sprintf(warn_buf, "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't use init-lambda to set lambda state (except for slow growth). Use init-lambda-state instead.");
 +                warning(wi, warn_buf);
 +            }
 +
 +            if (n_lambda_terms < 2 && fep->n_lambda > 0)
 +            {
 +                warning_note(wi,
 +                             "init-lambda is deprecated for setting lambda state (except for slow growth). Use init-lambda-state instead.");
 +            }
 +        }
 +
 +        for (j = 0; j < efptNR; j++)
 +        {
 +            for (i = 0; i < fep->n_lambda; i++)
 +            {
 +                sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[j], fep->all_lambda[j][i]);
 +                CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1));
 +            }
 +        }
 +
 +        if ((fep->sc_alpha > 0) && (!fep->bScCoul))
 +        {
 +            for (i = 0; i < fep->n_lambda; i++)
 +            {
 +                sprintf(err_buf, "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to crashes, and is not supported.", i, fep->all_lambda[efptVDW][i],
 +                        fep->all_lambda[efptCOUL][i]);
 +                CHECK((fep->sc_alpha > 0) &&
 +                      (((fep->all_lambda[efptCOUL][i] > 0.0) &&
 +                        (fep->all_lambda[efptCOUL][i] < 1.0)) &&
 +                       ((fep->all_lambda[efptVDW][i] > 0.0) &&
 +                        (fep->all_lambda[efptVDW][i] < 1.0))));
 +            }
 +        }
 +
 +        if ((fep->bScCoul) && (EEL_PME(ir->coulombtype)))
 +        {
 +            real sigma, lambda, r_sc;
 +
 +            sigma  = 0.34;
 +            /* Maximum estimate for A and B charges equal with lambda power 1 */
 +            lambda = 0.5;
 +            r_sc   = pow(lambda*fep->sc_alpha*pow(sigma/ir->rcoulomb, fep->sc_r_power) + 1.0, 1.0/fep->sc_r_power);
 +            sprintf(warn_buf, "With PME there is a minor soft core effect present at the cut-off, proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on energy conservation, but usually other effects dominate. With a common sigma value of %g nm the fraction of the particle-particle potential at the cut-off at lambda=%g is around %.1e, while ewald-rtol is %.1e.",
 +                    fep->sc_r_power,
 +                    sigma, lambda, r_sc - 1.0, ir->ewald_rtol);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        /*  Free Energy Checks -- In an ideal world, slow growth and FEP would
 +            be treated differently, but that's the next step */
 +
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                sprintf(err_buf, "%s[%d] must be between 0 and 1", efpt_names[i], j);
 +                CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1));
 +            }
 +        }
 +    }
 +
 +    if ((ir->bSimTemp) || (ir->efep == efepEXPANDED))
 +    {
 +        fep    = ir->fepvals;
 +        expand = ir->expandedvals;
 +
 +        /* checking equilibration of weights inputs for validity */
 +
 +        sprintf(err_buf, "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]);
 +        CHECK((expand->equil_n_at_lam > 0) && (expand->elmceq != elmceqNUMATLAM));
 +
 +        sprintf(err_buf, "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_samples, elmceq_names[elmceqSAMPLES]);
 +        CHECK((expand->equil_samples > 0) && (expand->elmceq != elmceqSAMPLES));
 +
 +        sprintf(err_buf, "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_steps, elmceq_names[elmceqSTEPS]);
 +        CHECK((expand->equil_steps > 0) && (expand->elmceq != elmceqSTEPS));
 +
 +        sprintf(err_buf, "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_samples, elmceq_names[elmceqWLDELTA]);
 +        CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != elmceqWLDELTA));
 +
 +        sprintf(err_buf, "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_ratio, elmceq_names[elmceqRATIO]);
 +        CHECK((expand->equil_ratio > 0) && (expand->elmceq != elmceqRATIO));
 +
 +        sprintf(err_buf, "weight-equil-number-all-lambda (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]);
 +        CHECK((expand->equil_n_at_lam <= 0) && (expand->elmceq == elmceqNUMATLAM));
 +
 +        sprintf(err_buf, "weight-equil-number-samples (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_samples, elmceq_names[elmceqSAMPLES]);
 +        CHECK((expand->equil_samples <= 0) && (expand->elmceq == elmceqSAMPLES));
 +
 +        sprintf(err_buf, "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_steps, elmceq_names[elmceqSTEPS]);
 +        CHECK((expand->equil_steps <= 0) && (expand->elmceq == elmceqSTEPS));
 +
 +        sprintf(err_buf, "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s",
 +                expand->equil_wl_delta, elmceq_names[elmceqWLDELTA]);
 +        CHECK((expand->equil_wl_delta <= 0) && (expand->elmceq == elmceqWLDELTA));
 +
 +        sprintf(err_buf, "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s",
 +                expand->equil_ratio, elmceq_names[elmceqRATIO]);
 +        CHECK((expand->equil_ratio <= 0) && (expand->elmceq == elmceqRATIO));
 +
 +        sprintf(err_buf, "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s",
 +                elmceq_names[elmceqWLDELTA], elamstats_names[elamstatsWL], elamstats_names[elamstatsWWL]);
 +        CHECK((expand->elmceq == elmceqWLDELTA) && (!EWL(expand->elamstats)));
 +
 +        sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats);
 +        CHECK((expand->lmc_repeats <= 0));
 +        sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin);
 +        CHECK((expand->minvarmin <= 0));
 +        sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range);
 +        CHECK((expand->c_range < 0));
 +        sprintf(err_buf, "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != 'no'",
 +                fep->init_fep_state, expand->lmc_forced_nstart);
 +        CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0) && (expand->elmcmove != elmcmoveNO));
 +        sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart);
 +        CHECK((expand->lmc_forced_nstart < 0));
 +        sprintf(err_buf, "init-lambda-state (%d) must be in the interval [0,number of lambdas)", fep->init_fep_state);
 +        CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda));
 +
 +        sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta);
 +        CHECK((expand->init_wl_delta < 0));
 +        sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio);
 +        CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1));
 +        sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale);
 +        CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1));
 +
 +        /* if there is no temperature control, we need to specify an MC temperature */
 +        sprintf(err_buf, "If there is no temperature control, and lmc-mcmove!= 'no',mc_temperature must be set to a positive number");
 +        if (expand->nstTij > 0)
 +        {
 +            sprintf(err_buf, "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)",
 +                    expand->nstTij, ir->nstlog);
 +            CHECK((mod(expand->nstTij, ir->nstlog) != 0));
 +        }
 +    }
 +
 +    /* PBC/WALLS */
 +    sprintf(err_buf, "walls only work with pbc=%s", epbc_names[epbcXY]);
 +    CHECK(ir->nwall && ir->ePBC != epbcXY);
 +
 +    /* VACUUM STUFF */
 +    if (ir->ePBC != epbcXYZ && ir->nwall != 2)
 +    {
 +        if (ir->ePBC == epbcNONE)
 +        {
 +            if (ir->epc != epcNO)
 +            {
 +                warning(wi, "Turning off pressure coupling for vacuum system");
 +                ir->epc = epcNO;
 +            }
 +        }
 +        else
 +        {
 +            sprintf(err_buf, "Can not have pressure coupling with pbc=%s",
 +                    epbc_names[ir->ePBC]);
 +            CHECK(ir->epc != epcNO);
 +        }
 +        sprintf(err_buf, "Can not have Ewald with pbc=%s", epbc_names[ir->ePBC]);
 +        CHECK(EEL_FULL(ir->coulombtype));
 +
 +        sprintf(err_buf, "Can not have dispersion correction with pbc=%s",
 +                epbc_names[ir->ePBC]);
 +        CHECK(ir->eDispCorr != edispcNO);
 +    }
 +
 +    if (ir->rlist == 0.0)
 +    {
 +        sprintf(err_buf, "can only have neighborlist cut-off zero (=infinite)\n"
 +                "with coulombtype = %s or coulombtype = %s\n"
 +                "without periodic boundary conditions (pbc = %s) and\n"
 +                "rcoulomb and rvdw set to zero",
 +                eel_names[eelCUT], eel_names[eelUSER], epbc_names[epbcNONE]);
 +        CHECK(((ir->coulombtype != eelCUT) && (ir->coulombtype != eelUSER)) ||
 +              (ir->ePBC     != epbcNONE) ||
 +              (ir->rcoulomb != 0.0)      || (ir->rvdw != 0.0));
 +
 +        if (ir->nstlist < 0)
 +        {
 +            warning_error(wi, "Can not have heuristic neighborlist updates without cut-off");
 +        }
 +        if (ir->nstlist > 0)
 +        {
 +            warning_note(wi, "Simulating without cut-offs can be (slightly) faster with nstlist=0, nstype=simple and only one MPI rank");
 +        }
 +    }
 +
 +    /* COMM STUFF */
 +    if (ir->nstcomm == 0)
 +    {
 +        ir->comm_mode = ecmNO;
 +    }
 +    if (ir->comm_mode != ecmNO)
 +    {
 +        if (ir->nstcomm < 0)
 +        {
 +            warning(wi, "If you want to remove the rotation around the center of mass, you should set comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to its absolute value");
 +            ir->nstcomm = abs(ir->nstcomm);
 +        }
 +
 +        if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy)
 +        {
 +            warning_note(wi, "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting nstcomm to nstcalcenergy");
 +            ir->nstcomm = ir->nstcalcenergy;
 +        }
 +
 +        if (ir->comm_mode == ecmANGULAR)
 +        {
 +            sprintf(err_buf, "Can not remove the rotation around the center of mass with periodic molecules");
 +            CHECK(ir->bPeriodicMols);
 +            if (ir->ePBC != epbcNONE)
 +            {
 +                warning(wi, "Removing the rotation around the center of mass in a periodic system (this is not a problem when you have only one molecule).");
 +            }
 +        }
 +    }
 +
 +    if (EI_STATE_VELOCITY(ir->eI) && ir->ePBC == epbcNONE && ir->comm_mode != ecmANGULAR)
 +    {
 +        warning_note(wi, "Tumbling and or flying ice-cubes: We are not removing rotation around center of mass in a non-periodic system. You should probably set comm_mode = ANGULAR.");
 +    }
 +
 +    sprintf(err_buf, "Twin-range neighbour searching (NS) with simple NS"
 +            " algorithm not implemented");
 +    CHECK(((ir->rcoulomb > ir->rlist) || (ir->rvdw > ir->rlist))
 +          && (ir->ns_type == ensSIMPLE));
 +
 +    /* TEMPERATURE COUPLING */
 +    if (ir->etc == etcYES)
 +    {
 +        ir->etc = etcBERENDSEN;
 +        warning_note(wi, "Old option for temperature coupling given: "
 +                     "changing \"yes\" to \"Berendsen\"\n");
 +    }
 +
 +    if ((ir->etc == etcNOSEHOOVER) || (ir->epc == epcMTTK))
 +    {
 +        if (ir->opts.nhchainlength < 1)
 +        {
 +            sprintf(warn_buf, "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to 1\n", ir->opts.nhchainlength);
 +            ir->opts.nhchainlength = 1;
 +            warning(wi, warn_buf);
 +        }
 +
 +        if (ir->etc == etcNOSEHOOVER && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1)
 +        {
 +            warning_note(wi, "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1");
 +            ir->opts.nhchainlength = 1;
 +        }
 +    }
 +    else
 +    {
 +        ir->opts.nhchainlength = 0;
 +    }
 +
 +    if (ir->eI == eiVVAK)
 +    {
 +        sprintf(err_buf, "%s implemented primarily for validation, and requires nsttcouple = 1 and nstpcouple = 1.",
 +                ei_names[eiVVAK]);
 +        CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1));
 +    }
 +
 +    if (ETC_ANDERSEN(ir->etc))
 +    {
 +        sprintf(err_buf, "%s temperature control not supported for integrator %s.", etcoupl_names[ir->etc], ei_names[ir->eI]);
 +        CHECK(!(EI_VV(ir->eI)));
 +
 +        if (ir->nstcomm > 0 && (ir->etc == etcANDERSEN))
 +        {
 +            sprintf(warn_buf, "Center of mass removal not necessary for %s.  All velocities of coupled groups are rerandomized periodically, so flying ice cube errors will not occur.", etcoupl_names[ir->etc]);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        sprintf(err_buf, "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are randomized every time step", ir->nstcomm, etcoupl_names[ir->etc]);
 +        CHECK(ir->nstcomm > 1 && (ir->etc == etcANDERSEN));
 +    }
 +
 +    if (ir->etc == etcBERENDSEN)
 +    {
 +        sprintf(warn_buf, "The %s thermostat does not generate the correct kinetic energy distribution. You might want to consider using the %s thermostat.",
 +                ETCOUPLTYPE(ir->etc), ETCOUPLTYPE(etcVRESCALE));
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if ((ir->etc == etcNOSEHOOVER || ETC_ANDERSEN(ir->etc))
 +        && ir->epc == epcBERENDSEN)
 +    {
 +        sprintf(warn_buf, "Using Berendsen pressure coupling invalidates the "
 +                "true ensemble for the thermostat");
 +        warning(wi, warn_buf);
 +    }
 +
 +    /* PRESSURE COUPLING */
 +    if (ir->epc == epcISOTROPIC)
 +    {
 +        ir->epc = epcBERENDSEN;
 +        warning_note(wi, "Old option for pressure coupling given: "
 +                     "changing \"Isotropic\" to \"Berendsen\"\n");
 +    }
 +
 +    if (ir->epc != epcNO)
 +    {
 +        dt_pcoupl = ir->nstpcouple*ir->delta_t;
 +
 +        sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p);
 +        CHECK(ir->tau_p <= 0);
 +
 +        if (ir->tau_p/dt_pcoupl < pcouple_min_integration_steps(ir->epc))
 +        {
 +            sprintf(warn_buf, "For proper integration of the %s barostat, tau-p (%g) should be at least %d times larger than nstpcouple*dt (%g)",
 +                    EPCOUPLTYPE(ir->epc), ir->tau_p, pcouple_min_integration_steps(ir->epc), dt_pcoupl);
 +            warning(wi, warn_buf);
 +        }
 +
 +        sprintf(err_buf, "compressibility must be > 0 when using pressure"
 +                " coupling %s\n", EPCOUPLTYPE(ir->epc));
 +        CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 ||
 +              ir->compress[ZZ][ZZ] < 0 ||
 +              (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 &&
 +               ir->compress[ZZ][XX] <= 0 && ir->compress[ZZ][YY] <= 0));
 +
 +        if (epcPARRINELLORAHMAN == ir->epc && opts->bGenVel)
 +        {
 +            sprintf(warn_buf,
 +                    "You are generating velocities so I am assuming you "
 +                    "are equilibrating a system. You are using "
 +                    "%s pressure coupling, but this can be "
 +                    "unstable for equilibration. If your system crashes, try "
 +                    "equilibrating first with Berendsen pressure coupling. If "
 +                    "you are not equilibrating the system, you can probably "
 +                    "ignore this warning.",
 +                    epcoupl_names[ir->epc]);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EI_VV(ir->eI))
 +    {
 +        if (ir->epc > epcNO)
 +        {
 +            if ((ir->epc != epcBERENDSEN) && (ir->epc != epcMTTK))
 +            {
 +                warning_error(wi, "for md-vv and md-vv-avek, can only use Berendsen and Martyna-Tuckerman-Tobias-Klein (MTTK) equations for pressure control; MTTK is equivalent to Parrinello-Rahman.");
 +            }
 +        }
 +    }
 +    else
 +    {
 +        if (ir->epc == epcMTTK)
 +        {
 +            warning_error(wi, "MTTK pressure coupling requires a Velocity-verlet integrator");
 +        }
 +    }
 +
 +    /* ELECTROSTATICS */
 +    /* More checks are in triple check (grompp.c) */
 +
 +    if (ir->coulombtype == eelSWITCH)
 +    {
 +        sprintf(warn_buf, "coulombtype = %s is only for testing purposes and can lead to serious "
 +                "artifacts, advice: use coulombtype = %s",
 +                eel_names[ir->coulombtype],
 +                eel_names[eelRF_ZERO]);
 +        warning(wi, warn_buf);
 +    }
 +
 +    if (ir->epsilon_r != 1 && ir->implicit_solvent == eisGBSA)
 +    {
 +        sprintf(warn_buf, "epsilon-r = %g with GB implicit solvent, will use this value for inner dielectric", ir->epsilon_r);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1)
 +    {
 +        sprintf(warn_buf, "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old format and exchanging epsilon-r and epsilon-rf", ir->epsilon_r);
 +        warning(wi, warn_buf);
 +        ir->epsilon_rf = ir->epsilon_r;
 +        ir->epsilon_r  = 1.0;
 +    }
 +
 +    if (getenv("GALACTIC_DYNAMICS") == NULL)
 +    {
 +        sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r);
 +        CHECK(ir->epsilon_r < 0);
 +    }
 +
 +    if (EEL_RF(ir->coulombtype))
 +    {
 +        /* reaction field (at the cut-off) */
 +
 +        if (ir->coulombtype == eelRF_ZERO)
 +        {
 +            sprintf(warn_buf, "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->epsilon_rf != 0);
 +            ir->epsilon_rf = 0.0;
 +        }
 +
 +        sprintf(err_buf, "epsilon-rf must be >= epsilon-r");
 +        CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) ||
 +              (ir->epsilon_r == 0));
 +        if (ir->epsilon_rf == ir->epsilon_r)
 +        {
 +            sprintf(warn_buf, "Using epsilon-rf = epsilon-r with %s does not make sense",
 +                    eel_names[ir->coulombtype]);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +    /* Allow rlist>rcoulomb for tabulated long range stuff. This just
 +     * means the interaction is zero outside rcoulomb, but it helps to
 +     * provide accurate energy conservation.
 +     */
 +    if (ir_coulomb_might_be_zero_at_cutoff(ir))
 +    {
 +        if (ir_coulomb_switched(ir))
 +        {
 +            sprintf(err_buf,
 +                    "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the potential modifier options!",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rcoulomb_switch >= ir->rcoulomb);
 +        }
 +    }
 +    else if (ir->coulombtype == eelCUT || EEL_RF(ir->coulombtype))
 +    {
 +        if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            sprintf(err_buf, "With coulombtype = %s, rcoulomb should be >= rlist unless you use a potential modifier",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rlist > ir->rcoulomb);
 +        }
 +    }
 +
 +    if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT ||
 +        ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT)
 +    {
 +        sprintf(warn_buf,
 +                "The switch/shift interaction settings are just for compatibility; you will get better "
 +                "performance from applying potential modifiers to your interactions!\n");
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if (ir->coulombtype == eelPMESWITCH || ir->coulomb_modifier == eintmodPOTSWITCH)
 +    {
 +        if (ir->rcoulomb_switch/ir->rcoulomb < 0.9499)
 +        {
 +            real percentage  = 100*(ir->rcoulomb-ir->rcoulomb_switch)/ir->rcoulomb;
 +            sprintf(warn_buf, "The switching range for should be 5%% or less (currently %.2f%% using a switching range of %4f-%4f) for accurate electrostatic energies, energy conservation will be good regardless, since ewald_rtol = %g.",
 +                    percentage, ir->rcoulomb_switch, ir->rcoulomb, ir->ewald_rtol);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (ir->vdwtype == evdwSWITCH || ir->vdw_modifier == eintmodPOTSWITCH)
 +    {
 +        if (ir->rvdw_switch == 0)
 +        {
 +            sprintf(warn_buf, "rvdw-switch is equal 0 even though you are using a switched Lennard-Jones potential.  This suggests it was not set in the mdp, which can lead to large energy errors.  In GROMACS, 0.05 to 0.1 nm is often a reasonable vdw switching range.");
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->coulombtype == eelPMESWITCH || ir->coulombtype == eelPMEUSER ||
 +            ir->coulombtype == eelPMEUSERSWITCH)
 +        {
 +            sprintf(err_buf, "With coulombtype = %s, rcoulomb must be <= rlist",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rcoulomb > ir->rlist);
 +        }
 +        else if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            if (ir->coulombtype == eelPME || ir->coulombtype == eelP3M_AD)
 +            {
 +                sprintf(err_buf,
 +                        "With coulombtype = %s (without modifier), rcoulomb must be equal to rlist,\n"
 +                        "or rlistlong if nstcalclr=1. For optimal energy conservation,consider using\n"
 +                        "a potential modifier.", eel_names[ir->coulombtype]);
 +                if (ir->nstcalclr == 1)
 +                {
 +                    CHECK(ir->rcoulomb != ir->rlist && ir->rcoulomb != ir->rlistlong);
 +                }
 +                else
 +                {
 +                    CHECK(ir->rcoulomb != ir->rlist);
 +                }
 +            }
 +        }
 +    }
 +
 +    if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype))
 +    {
 +        if (ir->pme_order < 3)
 +        {
 +            warning_error(wi, "pme-order can not be smaller than 3");
 +        }
 +    }
 +
 +    if (ir->nwall == 2 && EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->ewald_geometry == eewg3D)
 +        {
 +            sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s",
 +                    epbc_names[ir->ePBC], eewg_names[eewg3DC]);
 +            warning(wi, warn_buf);
 +        }
 +        /* This check avoids extra pbc coding for exclusion corrections */
 +        sprintf(err_buf, "wall-ewald-zfac should be >= 2");
 +        CHECK(ir->wall_ewald_zfac < 2);
 +    }
 +
 +    if (ir_vdw_switched(ir))
 +    {
 +        sprintf(err_buf, "With switched vdw forces or potentials, rvdw-switch must be < rvdw");
 +        CHECK(ir->rvdw_switch >= ir->rvdw);
 +
 +        if (ir->rvdw_switch < 0.5*ir->rvdw)
 +        {
 +            sprintf(warn_buf, "You are applying a switch function to vdw forces or potentials from %g to %g nm, which is more than half the interaction range, whereas switch functions are intended to act only close to the cut-off.",
 +                    ir->rvdw_switch, ir->rvdw);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +    else if (ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME)
 +    {
 +        if (ir->cutoff_scheme == ecutsGROUP && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(err_buf, "With vdwtype = %s, rvdw must be >= rlist unless you use a potential modifier", evdw_names[ir->vdwtype]);
 +            CHECK(ir->rlist > ir->rvdw);
 +        }
 +    }
 +
 +    if (ir->vdwtype == evdwPME)
 +    {
 +        if (!(ir->vdw_modifier == eintmodNONE || ir->vdw_modifier == eintmodPOTSHIFT))
 +        {
 +            sprintf(err_buf, "With vdwtype = %s, the only supported modifiers are %s a\
 +nd %s",
 +                    evdw_names[ir->vdwtype],
 +                    eintmod_names[eintmodPOTSHIFT],
 +                    eintmod_names[eintmodNONE]);
 +        }
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (((ir->coulomb_modifier != eintmodNONE && ir->rcoulomb == ir->rlist) ||
 +             (ir->vdw_modifier != eintmodNONE && ir->rvdw == ir->rlist)) &&
 +            ir->nstlist != 1)
 +        {
 +            warning_note(wi, "With exact cut-offs, rlist should be "
 +                         "larger than rcoulomb and rvdw, so that there "
 +                         "is a buffer region for particle motion "
 +                         "between neighborsearch steps");
 +        }
 +
 +        if (ir_coulomb_is_zero_at_cutoff(ir) && ir->rlistlong <= ir->rcoulomb)
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rcoulomb.",
 +                    IR_TWINRANGE(*ir) ? "rlistlong" : "rlist");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir_vdw_switched(ir) && (ir->rlistlong <= ir->rvdw))
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rvdw.",
 +                    IR_TWINRANGE(*ir) ? "rlistlong" : "rlist");
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    if (ir->vdwtype == evdwUSER && ir->eDispCorr != edispcNO)
 +    {
 +        warning_note(wi, "You have selected user tables with dispersion correction, the dispersion will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction between rvdw_switch and rvdw will not be double counted). Make sure that you really want dispersion correction to -C6/r^6.");
 +    }
 +
 +    if (ir->nstlist == -1)
 +    {
 +        sprintf(err_buf, "With nstlist=-1 rvdw and rcoulomb should be smaller than rlist to account for diffusion and possibly charge-group radii");
 +        CHECK(ir->rvdw >= ir->rlist || ir->rcoulomb >= ir->rlist);
 +    }
 +    sprintf(err_buf, "nstlist can not be smaller than -1");
 +    CHECK(ir->nstlist < -1);
 +
 +    if (ir->eI == eiLBFGS && (ir->coulombtype == eelCUT || ir->vdwtype == evdwCUT)
 +        && ir->rvdw != 0)
 +    {
 +        warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off.");
 +    }
 +
 +    if (ir->eI == eiLBFGS && ir->nbfgscorr <= 0)
 +    {
 +        warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent.");
 +    }
 +
 +    /* ENERGY CONSERVATION */
 +    if (ir_NVE(ir) && ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (!ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > 0 && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for VdW interactions with NVE, for good energy conservation use vdwtype = %s (possibly with DispCorr)",
 +                    evdw_names[evdwSHIFT]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (!ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > 0)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for electrostatics with NVE, for good energy conservation use coulombtype = %s or %s",
 +                    eel_names[eelPMESWITCH], eel_names[eelRF_ZERO]);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
++    if (EI_VV(ir->eI) && IR_TWINRANGE(*ir) && ir->nstlist > 1)
++    {
++        sprintf(warn_buf, "Twin-range multiple time stepping does not work with integrator %s.", ei_names[ir->eI]);
++        warning_error(wi, warn_buf);
++    }
++
 +    /* IMPLICIT SOLVENT */
 +    if (ir->coulombtype == eelGB_NOTUSED)
 +    {
 +        ir->coulombtype      = eelCUT;
 +        ir->implicit_solvent = eisGBSA;
 +        fprintf(stderr, "Note: Old option for generalized born electrostatics given:\n"
 +                "Changing coulombtype from \"generalized-born\" to \"cut-off\" and instead\n"
 +                "setting implicit-solvent value to \"GBSA\" in input section.\n");
 +    }
 +
 +    if (ir->sa_algorithm == esaSTILL)
 +    {
 +        sprintf(err_buf, "Still SA algorithm not available yet, use %s or %s instead\n", esa_names[esaAPPROX], esa_names[esaNO]);
 +        CHECK(ir->sa_algorithm == esaSTILL);
 +    }
 +
 +    if (ir->implicit_solvent == eisGBSA)
 +    {
 +        sprintf(err_buf, "With GBSA implicit solvent, rgbradii must be equal to rlist.");
 +        CHECK(ir->rgbradii != ir->rlist);
 +
 +        if (ir->coulombtype != eelCUT)
 +        {
 +            sprintf(err_buf, "With GBSA, coulombtype must be equal to %s\n", eel_names[eelCUT]);
 +            CHECK(ir->coulombtype != eelCUT);
 +        }
 +        if (ir->vdwtype != evdwCUT)
 +        {
 +            sprintf(err_buf, "With GBSA, vdw-type must be equal to %s\n", evdw_names[evdwCUT]);
 +            CHECK(ir->vdwtype != evdwCUT);
 +        }
 +        if (ir->nstgbradii < 1)
 +        {
 +            sprintf(warn_buf, "Using GBSA with nstgbradii<1, setting nstgbradii=1");
 +            warning_note(wi, warn_buf);
 +            ir->nstgbradii = 1;
 +        }
 +        if (ir->sa_algorithm == esaNO)
 +        {
 +            sprintf(warn_buf, "No SA (non-polar) calculation requested together with GB. Are you sure this is what you want?\n");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir->sa_surface_tension < 0 && ir->sa_algorithm != esaNO)
 +        {
 +            sprintf(warn_buf, "Value of sa_surface_tension is < 0. Changing it to 2.05016 or 2.25936 kJ/nm^2/mol for Still and HCT/OBC respectively\n");
 +            warning_note(wi, warn_buf);
 +
 +            if (ir->gb_algorithm == egbSTILL)
 +            {
 +                ir->sa_surface_tension = 0.0049 * CAL2JOULE * 100;
 +            }
 +            else
 +            {
 +                ir->sa_surface_tension = 0.0054 * CAL2JOULE * 100;
 +            }
 +        }
 +        if (ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO)
 +        {
 +            sprintf(err_buf, "Surface tension set to 0 while SA-calculation requested\n");
 +            CHECK(ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO);
 +        }
 +
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        if (ir->cutoff_scheme != ecutsGROUP)
 +        {
 +            warning_error(wi, "AdresS simulation supports only cutoff-scheme=group");
 +        }
 +        if (!EI_SD(ir->eI))
 +        {
 +            warning_error(wi, "AdresS simulation supports only stochastic dynamics");
 +        }
 +        if (ir->epc != epcNO)
 +        {
 +            warning_error(wi, "AdresS simulation does not support pressure coupling");
 +        }
 +        if (EEL_FULL(ir->coulombtype))
 +        {
 +            warning_error(wi, "AdresS simulation does not support long-range electrostatics");
 +        }
 +    }
 +}
 +
 +/* count the number of text elemets separated by whitespace in a string.
 +    str = the input string
 +    maxptr = the maximum number of allowed elements
 +    ptr = the output array of pointers to the first character of each element
 +    returns: the number of elements. */
 +int str_nelem(const char *str, int maxptr, char *ptr[])
 +{
 +    int   np = 0;
 +    char *copy0, *copy;
 +
 +    copy0 = strdup(str);
 +    copy  = copy0;
 +    ltrim(copy);
 +    while (*copy != '\0')
 +    {
 +        if (np >= maxptr)
 +        {
 +            gmx_fatal(FARGS, "Too many groups on line: '%s' (max is %d)",
 +                      str, maxptr);
 +        }
 +        if (ptr)
 +        {
 +            ptr[np] = copy;
 +        }
 +        np++;
 +        while ((*copy != '\0') && !isspace(*copy))
 +        {
 +            copy++;
 +        }
 +        if (*copy != '\0')
 +        {
 +            *copy = '\0';
 +            copy++;
 +        }
 +        ltrim(copy);
 +    }
 +    if (ptr == NULL)
 +    {
 +        sfree(copy0);
 +    }
 +
 +    return np;
 +}
 +
 +/* interpret a number of doubles from a string and put them in an array,
 +   after allocating space for them.
 +   str = the input string
 +   n = the (pre-allocated) number of doubles read
 +   r = the output array of doubles. */
 +static void parse_n_real(char *str, int *n, real **r)
 +{
 +    char *ptr[MAXPTR];
 +    int   i;
 +
 +    *n = str_nelem(str, MAXPTR, ptr);
 +
 +    snew(*r, *n);
 +    for (i = 0; i < *n; i++)
 +    {
 +        (*r)[i] = strtod(ptr[i], NULL);
 +    }
 +}
 +
 +static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN], char weights[STRLEN])
 +{
 +
 +    int         i, j, max_n_lambda, nweights, nfep[efptNR];
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +    real      **count_fep_lambdas;
 +    gmx_bool    bOneLambda = TRUE;
 +
 +    snew(count_fep_lambdas, efptNR);
 +
 +    /* FEP input processing */
 +    /* first, identify the number of lambda values for each type.
 +       All that are nonzero must have the same number */
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        parse_n_real(fep_lambda[i], &(nfep[i]), &(count_fep_lambdas[i]));
 +    }
 +
 +    /* now, determine the number of components.  All must be either zero, or equal. */
 +
 +    max_n_lambda = 0;
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (nfep[i] > max_n_lambda)
 +        {
 +            max_n_lambda = nfep[i];  /* here's a nonzero one.  All of them
 +                                        must have the same number if its not zero.*/
 +            break;
 +        }
 +    }
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (nfep[i] == 0)
 +        {
 +            ir->fepvals->separate_dvdl[i] = FALSE;
 +        }
 +        else if (nfep[i] == max_n_lambda)
 +        {
 +            if (i != efptTEMPERATURE)  /* we treat this differently -- not really a reason to compute the derivative with
 +                                          respect to the temperature currently */
 +            {
 +                ir->fepvals->separate_dvdl[i] = TRUE;
 +            }
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Number of lambdas (%d) for FEP type %s not equal to number of other types (%d)",
 +                      nfep[i], efpt_names[i], max_n_lambda);
 +        }
 +    }
 +    /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */
 +    ir->fepvals->separate_dvdl[efptTEMPERATURE] = FALSE;
 +
 +    /* the number of lambdas is the number we've read in, which is either zero
 +       or the same for all */
 +    fep->n_lambda = max_n_lambda;
 +
 +    /* allocate space for the array of lambda values */
 +    snew(fep->all_lambda, efptNR);
 +    /* if init_lambda is defined, we need to set lambda */
 +    if ((fep->init_lambda > 0) && (fep->n_lambda == 0))
 +    {
 +        ir->fepvals->separate_dvdl[efptFEP] = TRUE;
 +    }
 +    /* otherwise allocate the space for all of the lambdas, and transfer the data */
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        snew(fep->all_lambda[i], fep->n_lambda);
 +        if (nfep[i] > 0)  /* if it's zero, then the count_fep_lambda arrays
 +                             are zero */
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                fep->all_lambda[i][j] = (double)count_fep_lambdas[i][j];
 +            }
 +            sfree(count_fep_lambdas[i]);
 +        }
 +    }
 +    sfree(count_fep_lambdas);
 +
 +    /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for internal
 +       bookkeeping -- for now, init_lambda */
 +
 +    if ((nfep[efptFEP] == 0) && (fep->init_lambda >= 0))
 +    {
 +        for (i = 0; i < fep->n_lambda; i++)
 +        {
 +            fep->all_lambda[efptFEP][i] = fep->init_lambda;
 +        }
 +    }
 +
 +    /* check to see if only a single component lambda is defined, and soft core is defined.
 +       In this case, turn on coulomb soft core */
 +
 +    if (max_n_lambda == 0)
 +    {
 +        bOneLambda = TRUE;
 +    }
 +    else
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            if ((nfep[i] != 0) && (i != efptFEP))
 +            {
 +                bOneLambda = FALSE;
 +            }
 +        }
 +    }
 +    if ((bOneLambda) && (fep->sc_alpha > 0))
 +    {
 +        fep->bScCoul = TRUE;
 +    }
 +
 +    /* Fill in the others with the efptFEP if they are not explicitly
 +       specified (i.e. nfep[i] == 0).  This means if fep is not defined,
 +       they are all zero. */
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if ((nfep[i] == 0) && (i != efptFEP))
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                fep->all_lambda[i][j] = fep->all_lambda[efptFEP][j];
 +            }
 +        }
 +    }
 +
 +
 +    /* make it easier if sc_r_power = 48 by increasing it to the 4th power, to be in the right scale. */
 +    if (fep->sc_r_power == 48)
 +    {
 +        if (fep->sc_alpha > 0.1)
 +        {
 +            gmx_fatal(FARGS, "sc_alpha (%f) for sc_r_power = 48 should usually be between 0.001 and 0.004", fep->sc_alpha);
 +        }
 +    }
 +
 +    expand = ir->expandedvals;
 +    /* now read in the weights */
 +    parse_n_real(weights, &nweights, &(expand->init_lambda_weights));
 +    if (nweights == 0)
 +    {
 +        snew(expand->init_lambda_weights, fep->n_lambda); /* initialize to zero */
 +    }
 +    else if (nweights != fep->n_lambda)
 +    {
 +        gmx_fatal(FARGS, "Number of weights (%d) is not equal to number of lambda values (%d)",
 +                  nweights, fep->n_lambda);
 +    }
 +    if ((expand->nstexpanded < 0) && (ir->efep != efepNO))
 +    {
 +        expand->nstexpanded = fep->nstdhdl;
 +        /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */
 +    }
 +    if ((expand->nstexpanded < 0) && ir->bSimTemp)
 +    {
 +        expand->nstexpanded = 2*(int)(ir->opts.tau_t[0]/ir->delta_t);
 +        /* if you don't specify nstexpanded when doing expanded ensemble simulated tempering, it is set to
 +           2*tau_t just to be careful so it's not to frequent  */
 +    }
 +}
 +
 +
 +static void do_simtemp_params(t_inputrec *ir)
 +{
 +
 +    snew(ir->simtempvals->temperatures, ir->fepvals->n_lambda);
 +    GetSimTemps(ir->fepvals->n_lambda, ir->simtempvals, ir->fepvals->all_lambda[efptTEMPERATURE]);
 +
 +    return;
 +}
 +
 +static void do_wall_params(t_inputrec *ir,
 +                           char *wall_atomtype, char *wall_density,
 +                           t_gromppopts *opts)
 +{
 +    int    nstr, i;
 +    char  *names[MAXPTR];
 +    double dbl;
 +
 +    opts->wall_atomtype[0] = NULL;
 +    opts->wall_atomtype[1] = NULL;
 +
 +    ir->wall_atomtype[0] = -1;
 +    ir->wall_atomtype[1] = -1;
 +    ir->wall_density[0]  = 0;
 +    ir->wall_density[1]  = 0;
 +
 +    if (ir->nwall > 0)
 +    {
 +        nstr = str_nelem(wall_atomtype, MAXPTR, names);
 +        if (nstr != ir->nwall)
 +        {
 +            gmx_fatal(FARGS, "Expected %d elements for wall_atomtype, found %d",
 +                      ir->nwall, nstr);
 +        }
 +        for (i = 0; i < ir->nwall; i++)
 +        {
 +            opts->wall_atomtype[i] = strdup(names[i]);
 +        }
 +
 +        if (ir->wall_type == ewt93 || ir->wall_type == ewt104)
 +        {
 +            nstr = str_nelem(wall_density, MAXPTR, names);
 +            if (nstr != ir->nwall)
 +            {
 +                gmx_fatal(FARGS, "Expected %d elements for wall-density, found %d", ir->nwall, nstr);
 +            }
 +            for (i = 0; i < ir->nwall; i++)
 +            {
 +                sscanf(names[i], "%lf", &dbl);
 +                if (dbl <= 0)
 +                {
 +                    gmx_fatal(FARGS, "wall-density[%d] = %f\n", i, dbl);
 +                }
 +                ir->wall_density[i] = dbl;
 +            }
 +        }
 +    }
 +}
 +
 +static void add_wall_energrps(gmx_groups_t *groups, int nwall, t_symtab *symtab)
 +{
 +    int     i;
 +    t_grps *grps;
 +    char    str[STRLEN];
 +
 +    if (nwall > 0)
 +    {
 +        srenew(groups->grpname, groups->ngrpname+nwall);
 +        grps = &(groups->grps[egcENER]);
 +        srenew(grps->nm_ind, grps->nr+nwall);
 +        for (i = 0; i < nwall; i++)
 +        {
 +            sprintf(str, "wall%d", i);
 +            groups->grpname[groups->ngrpname] = put_symtab(symtab, str);
 +            grps->nm_ind[grps->nr++]          = groups->ngrpname++;
 +        }
 +    }
 +}
 +
 +void read_expandedparams(int *ninp_p, t_inpfile **inp_p,
 +                         t_expanded *expand, warninp_t wi)
 +{
 +    int        ninp, nerror = 0;
 +    t_inpfile *inp;
 +
 +    ninp   = *ninp_p;
 +    inp    = *inp_p;
 +
 +    /* read expanded ensemble parameters */
 +    CCTYPE ("expanded ensemble variables");
 +    ITYPE ("nstexpanded", expand->nstexpanded, -1);
 +    EETYPE("lmc-stats", expand->elamstats, elamstats_names);
 +    EETYPE("lmc-move", expand->elmcmove, elmcmove_names);
 +    EETYPE("lmc-weights-equil", expand->elmceq, elmceq_names);
 +    ITYPE ("weight-equil-number-all-lambda", expand->equil_n_at_lam, -1);
 +    ITYPE ("weight-equil-number-samples", expand->equil_samples, -1);
 +    ITYPE ("weight-equil-number-steps", expand->equil_steps, -1);
 +    RTYPE ("weight-equil-wl-delta", expand->equil_wl_delta, -1);
 +    RTYPE ("weight-equil-count-ratio", expand->equil_ratio, -1);
 +    CCTYPE("Seed for Monte Carlo in lambda space");
 +    ITYPE ("lmc-seed", expand->lmc_seed, -1);
 +    RTYPE ("mc-temperature", expand->mc_temp, -1);
 +    ITYPE ("lmc-repeats", expand->lmc_repeats, 1);
 +    ITYPE ("lmc-gibbsdelta", expand->gibbsdeltalam, -1);
 +    ITYPE ("lmc-forced-nstart", expand->lmc_forced_nstart, 0);
 +    EETYPE("symmetrized-transition-matrix", expand->bSymmetrizedTMatrix, yesno_names);
 +    ITYPE("nst-transition-matrix", expand->nstTij, -1);
 +    ITYPE ("mininum-var-min", expand->minvarmin, 100); /*default is reasonable */
 +    ITYPE ("weight-c-range", expand->c_range, 0);      /* default is just C=0 */
 +    RTYPE ("wl-scale", expand->wl_scale, 0.8);
 +    RTYPE ("wl-ratio", expand->wl_ratio, 0.8);
 +    RTYPE ("init-wl-delta", expand->init_wl_delta, 1.0);
 +    EETYPE("wl-oneovert", expand->bWLoneovert, yesno_names);
 +
 +    *ninp_p   = ninp;
 +    *inp_p    = inp;
 +
 +    return;
 +}
 +
 +void get_ir(const char *mdparin, const char *mdparout,
 +            t_inputrec *ir, t_gromppopts *opts,
 +            warninp_t wi)
 +{
 +    char       *dumstr[2];
 +    double      dumdub[2][6];
 +    t_inpfile  *inp;
 +    const char *tmp;
 +    int         i, j, m, ninp;
 +    char        warn_buf[STRLEN];
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    init_inputrec_strings();
 +    inp = read_inpfile(mdparin, &ninp, wi);
 +
 +    snew(dumstr[0], STRLEN);
 +    snew(dumstr[1], STRLEN);
 +
 +    if (-1 == search_einp(ninp, inp, "cutoff-scheme"))
 +    {
 +        sprintf(warn_buf,
 +                "%s did not specify a value for the .mdp option "
 +                "\"cutoff-scheme\". Probably it was first intended for use "
 +                "with GROMACS before 4.6. In 4.6, the Verlet scheme was "
 +                "introduced, but the group scheme was still the default. "
 +                "The default is now the Verlet scheme, so you will observe "
 +                "different behaviour.", mdparin);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    /* remove the following deprecated commands */
 +    REM_TYPE("title");
 +    REM_TYPE("cpp");
 +    REM_TYPE("domain-decomposition");
 +    REM_TYPE("andersen-seed");
 +    REM_TYPE("dihre");
 +    REM_TYPE("dihre-fc");
 +    REM_TYPE("dihre-tau");
 +    REM_TYPE("nstdihreout");
 +    REM_TYPE("nstcheckpoint");
 +
 +    /* replace the following commands with the clearer new versions*/
 +    REPL_TYPE("unconstrained-start", "continuation");
 +    REPL_TYPE("foreign-lambda", "fep-lambdas");
 +    REPL_TYPE("verlet-buffer-drift", "verlet-buffer-tolerance");
 +    REPL_TYPE("nstxtcout", "nstxout-compressed");
 +    REPL_TYPE("xtc-grps", "compressed-x-grps");
 +    REPL_TYPE("xtc-precision", "compressed-x-precision");
 +
 +    CCTYPE ("VARIOUS PREPROCESSING OPTIONS");
 +    CTYPE ("Preprocessor information: use cpp syntax.");
 +    CTYPE ("e.g.: -I/home/joe/doe -I/home/mary/roe");
 +    STYPE ("include", opts->include,  NULL);
 +    CTYPE ("e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)");
 +    STYPE ("define",  opts->define,   NULL);
 +
 +    CCTYPE ("RUN CONTROL PARAMETERS");
 +    EETYPE("integrator",  ir->eI,         ei_names);
 +    CTYPE ("Start time and timestep in ps");
 +    RTYPE ("tinit",   ir->init_t, 0.0);
 +    RTYPE ("dt",      ir->delta_t,    0.001);
 +    STEPTYPE ("nsteps",   ir->nsteps,     0);
 +    CTYPE ("For exact run continuation or redoing part of a run");
 +    STEPTYPE ("init-step", ir->init_step,  0);
 +    CTYPE ("Part index is updated automatically on checkpointing (keeps files separate)");
 +    ITYPE ("simulation-part", ir->simulation_part, 1);
 +    CTYPE ("mode for center of mass motion removal");
 +    EETYPE("comm-mode",   ir->comm_mode,  ecm_names);
 +    CTYPE ("number of steps for center of mass motion removal");
 +    ITYPE ("nstcomm", ir->nstcomm,    100);
 +    CTYPE ("group(s) for center of mass motion removal");
 +    STYPE ("comm-grps",   is->vcm,            NULL);
 +
 +    CCTYPE ("LANGEVIN DYNAMICS OPTIONS");
 +    CTYPE ("Friction coefficient (amu/ps) and random seed");
 +    RTYPE ("bd-fric",     ir->bd_fric,    0.0);
 +    STEPTYPE ("ld-seed",  ir->ld_seed,    -1);
 +
 +    /* Em stuff */
 +    CCTYPE ("ENERGY MINIMIZATION OPTIONS");
 +    CTYPE ("Force tolerance and initial step-size");
 +    RTYPE ("emtol",       ir->em_tol,     10.0);
 +    RTYPE ("emstep",      ir->em_stepsize, 0.01);
 +    CTYPE ("Max number of iterations in relax-shells");
 +    ITYPE ("niter",       ir->niter,      20);
 +    CTYPE ("Step size (ps^2) for minimization of flexible constraints");
 +    RTYPE ("fcstep",      ir->fc_stepsize, 0);
 +    CTYPE ("Frequency of steepest descents steps when doing CG");
 +    ITYPE ("nstcgsteep",  ir->nstcgsteep, 1000);
 +    ITYPE ("nbfgscorr",   ir->nbfgscorr,  10);
 +
 +    CCTYPE ("TEST PARTICLE INSERTION OPTIONS");
 +    RTYPE ("rtpi",    ir->rtpi,   0.05);
 +
 +    /* Output options */
 +    CCTYPE ("OUTPUT CONTROL OPTIONS");
 +    CTYPE ("Output frequency for coords (x), velocities (v) and forces (f)");
 +    ITYPE ("nstxout", ir->nstxout,    0);
 +    ITYPE ("nstvout", ir->nstvout,    0);
 +    ITYPE ("nstfout", ir->nstfout,    0);
 +    ir->nstcheckpoint = 1000;
 +    CTYPE ("Output frequency for energies to log file and energy file");
 +    ITYPE ("nstlog",  ir->nstlog, 1000);
 +    ITYPE ("nstcalcenergy", ir->nstcalcenergy, 100);
 +    ITYPE ("nstenergy",   ir->nstenergy,  1000);
 +    CTYPE ("Output frequency and precision for .xtc file");
 +    ITYPE ("nstxout-compressed", ir->nstxout_compressed,  0);
 +    RTYPE ("compressed-x-precision", ir->x_compression_precision, 1000.0);
 +    CTYPE ("This selects the subset of atoms for the compressed");
 +    CTYPE ("trajectory file. You can select multiple groups. By");
 +    CTYPE ("default, all atoms will be written.");
 +    STYPE ("compressed-x-grps", is->x_compressed_groups, NULL);
 +    CTYPE ("Selection of energy groups");
 +    STYPE ("energygrps",  is->energy,         NULL);
 +
 +    /* Neighbor searching */
 +    CCTYPE ("NEIGHBORSEARCHING PARAMETERS");
 +    CTYPE ("cut-off scheme (Verlet: particle based cut-offs, group: using charge groups)");
 +    EETYPE("cutoff-scheme",     ir->cutoff_scheme,    ecutscheme_names);
 +    CTYPE ("nblist update frequency");
 +    ITYPE ("nstlist", ir->nstlist,    10);
 +    CTYPE ("ns algorithm (simple or grid)");
 +    EETYPE("ns-type",     ir->ns_type,    ens_names);
 +    /* set ndelta to the optimal value of 2 */
 +    ir->ndelta = 2;
 +    CTYPE ("Periodic boundary conditions: xyz, no, xy");
 +    EETYPE("pbc",         ir->ePBC,       epbc_names);
 +    EETYPE("periodic-molecules", ir->bPeriodicMols, yesno_names);
 +    CTYPE ("Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,");
 +    CTYPE ("a value of -1 means: use rlist");
 +    RTYPE("verlet-buffer-tolerance", ir->verletbuf_tol,    0.005);
 +    CTYPE ("nblist cut-off");
 +    RTYPE ("rlist",   ir->rlist,  1.0);
 +    CTYPE ("long-range cut-off for switched potentials");
 +    RTYPE ("rlistlong",   ir->rlistlong,  -1);
 +    ITYPE ("nstcalclr",   ir->nstcalclr,  -1);
 +
 +    /* Electrostatics */
 +    CCTYPE ("OPTIONS FOR ELECTROSTATICS AND VDW");
 +    CTYPE ("Method for doing electrostatics");
 +    EETYPE("coulombtype", ir->coulombtype,    eel_names);
 +    EETYPE("coulomb-modifier",    ir->coulomb_modifier,    eintmod_names);
 +    CTYPE ("cut-off lengths");
 +    RTYPE ("rcoulomb-switch", ir->rcoulomb_switch,    0.0);
 +    RTYPE ("rcoulomb",    ir->rcoulomb,   1.0);
 +    CTYPE ("Relative dielectric constant for the medium and the reaction field");
 +    RTYPE ("epsilon-r",   ir->epsilon_r,  1.0);
 +    RTYPE ("epsilon-rf",  ir->epsilon_rf, 0.0);
 +    CTYPE ("Method for doing Van der Waals");
 +    EETYPE("vdw-type",    ir->vdwtype,    evdw_names);
 +    EETYPE("vdw-modifier",    ir->vdw_modifier,    eintmod_names);
 +    CTYPE ("cut-off lengths");
 +    RTYPE ("rvdw-switch", ir->rvdw_switch,    0.0);
 +    RTYPE ("rvdw",    ir->rvdw,   1.0);
 +    CTYPE ("Apply long range dispersion corrections for Energy and Pressure");
 +    EETYPE("DispCorr",    ir->eDispCorr,  edispc_names);
 +    CTYPE ("Extension of the potential lookup tables beyond the cut-off");
 +    RTYPE ("table-extension", ir->tabext, 1.0);
 +    CTYPE ("Separate tables between energy group pairs");
 +    STYPE ("energygrp-table", is->egptable,   NULL);
 +    CTYPE ("Spacing for the PME/PPPM FFT grid");
 +    RTYPE ("fourierspacing", ir->fourier_spacing, 0.12);
 +    CTYPE ("FFT grid size, when a value is 0 fourierspacing will be used");
 +    ITYPE ("fourier-nx",  ir->nkx,         0);
 +    ITYPE ("fourier-ny",  ir->nky,         0);
 +    ITYPE ("fourier-nz",  ir->nkz,         0);
 +    CTYPE ("EWALD/PME/PPPM parameters");
 +    ITYPE ("pme-order",   ir->pme_order,   4);
 +    RTYPE ("ewald-rtol",  ir->ewald_rtol, 0.00001);
 +    RTYPE ("ewald-rtol-lj", ir->ewald_rtol_lj, 0.001);
 +    EETYPE("lj-pme-comb-rule", ir->ljpme_combination_rule, eljpme_names);
 +    EETYPE("ewald-geometry", ir->ewald_geometry, eewg_names);
 +    RTYPE ("epsilon-surface", ir->epsilon_surface, 0.0);
 +    EETYPE("optimize-fft", ir->bOptFFT,  yesno_names);
 +
 +    CCTYPE("IMPLICIT SOLVENT ALGORITHM");
 +    EETYPE("implicit-solvent", ir->implicit_solvent, eis_names);
 +
 +    CCTYPE ("GENERALIZED BORN ELECTROSTATICS");
 +    CTYPE ("Algorithm for calculating Born radii");
 +    EETYPE("gb-algorithm", ir->gb_algorithm, egb_names);
 +    CTYPE ("Frequency of calculating the Born radii inside rlist");
 +    ITYPE ("nstgbradii", ir->nstgbradii, 1);
 +    CTYPE ("Cutoff for Born radii calculation; the contribution from atoms");
 +    CTYPE ("between rlist and rgbradii is updated every nstlist steps");
 +    RTYPE ("rgbradii",  ir->rgbradii, 1.0);
 +    CTYPE ("Dielectric coefficient of the implicit solvent");
 +    RTYPE ("gb-epsilon-solvent", ir->gb_epsilon_solvent, 80.0);
 +    CTYPE ("Salt concentration in M for Generalized Born models");
 +    RTYPE ("gb-saltconc",  ir->gb_saltconc, 0.0);
 +    CTYPE ("Scaling factors used in the OBC GB model. Default values are OBC(II)");
 +    RTYPE ("gb-obc-alpha", ir->gb_obc_alpha, 1.0);
 +    RTYPE ("gb-obc-beta", ir->gb_obc_beta, 0.8);
 +    RTYPE ("gb-obc-gamma", ir->gb_obc_gamma, 4.85);
 +    RTYPE ("gb-dielectric-offset", ir->gb_dielectric_offset, 0.009);
 +    EETYPE("sa-algorithm", ir->sa_algorithm, esa_names);
 +    CTYPE ("Surface tension (kJ/mol/nm^2) for the SA (nonpolar surface) part of GBSA");
 +    CTYPE ("The value -1 will set default value for Still/HCT/OBC GB-models.");
 +    RTYPE ("sa-surface-tension", ir->sa_surface_tension, -1);
 +
 +    /* Coupling stuff */
 +    CCTYPE ("OPTIONS FOR WEAK COUPLING ALGORITHMS");
 +    CTYPE ("Temperature coupling");
 +    EETYPE("tcoupl",  ir->etc,        etcoupl_names);
 +    ITYPE ("nsttcouple", ir->nsttcouple,  -1);
 +    ITYPE("nh-chain-length",     ir->opts.nhchainlength, 10);
 +    EETYPE("print-nose-hoover-chain-variables", ir->bPrintNHChains, yesno_names);
 +    CTYPE ("Groups to couple separately");
 +    STYPE ("tc-grps",     is->tcgrps,         NULL);
 +    CTYPE ("Time constant (ps) and reference temperature (K)");
 +    STYPE ("tau-t",   is->tau_t,      NULL);
 +    STYPE ("ref-t",   is->ref_t,      NULL);
 +    CTYPE ("pressure coupling");
 +    EETYPE("pcoupl",  ir->epc,        epcoupl_names);
 +    EETYPE("pcoupltype",  ir->epct,       epcoupltype_names);
 +    ITYPE ("nstpcouple", ir->nstpcouple,  -1);
 +    CTYPE ("Time constant (ps), compressibility (1/bar) and reference P (bar)");
 +    RTYPE ("tau-p",   ir->tau_p,  1.0);
 +    STYPE ("compressibility", dumstr[0],  NULL);
 +    STYPE ("ref-p",       dumstr[1],      NULL);
 +    CTYPE ("Scaling of reference coordinates, No, All or COM");
 +    EETYPE ("refcoord-scaling", ir->refcoord_scaling, erefscaling_names);
 +
 +    /* QMMM */
 +    CCTYPE ("OPTIONS FOR QMMM calculations");
 +    EETYPE("QMMM", ir->bQMMM, yesno_names);
 +    CTYPE ("Groups treated Quantum Mechanically");
 +    STYPE ("QMMM-grps",  is->QMMM,          NULL);
 +    CTYPE ("QM method");
 +    STYPE("QMmethod",     is->QMmethod, NULL);
 +    CTYPE ("QMMM scheme");
 +    EETYPE("QMMMscheme",  ir->QMMMscheme,    eQMMMscheme_names);
 +    CTYPE ("QM basisset");
 +    STYPE("QMbasis",      is->QMbasis, NULL);
 +    CTYPE ("QM charge");
 +    STYPE ("QMcharge",    is->QMcharge, NULL);
 +    CTYPE ("QM multiplicity");
 +    STYPE ("QMmult",      is->QMmult, NULL);
 +    CTYPE ("Surface Hopping");
 +    STYPE ("SH",          is->bSH, NULL);
 +    CTYPE ("CAS space options");
 +    STYPE ("CASorbitals",      is->CASorbitals,   NULL);
 +    STYPE ("CASelectrons",     is->CASelectrons,  NULL);
 +    STYPE ("SAon", is->SAon, NULL);
 +    STYPE ("SAoff", is->SAoff, NULL);
 +    STYPE ("SAsteps", is->SAsteps, NULL);
 +    CTYPE ("Scale factor for MM charges");
 +    RTYPE ("MMChargeScaleFactor", ir->scalefactor, 1.0);
 +    CTYPE ("Optimization of QM subsystem");
 +    STYPE ("bOPT",          is->bOPT, NULL);
 +    STYPE ("bTS",          is->bTS, NULL);
 +
 +    /* Simulated annealing */
 +    CCTYPE("SIMULATED ANNEALING");
 +    CTYPE ("Type of annealing for each temperature group (no/single/periodic)");
 +    STYPE ("annealing",   is->anneal,      NULL);
 +    CTYPE ("Number of time points to use for specifying annealing in each group");
 +    STYPE ("annealing-npoints", is->anneal_npoints, NULL);
 +    CTYPE ("List of times at the annealing points for each group");
 +    STYPE ("annealing-time",       is->anneal_time,       NULL);
 +    CTYPE ("Temp. at each annealing point, for each group.");
 +    STYPE ("annealing-temp",  is->anneal_temp,  NULL);
 +
 +    /* Startup run */
 +    CCTYPE ("GENERATE VELOCITIES FOR STARTUP RUN");
 +    EETYPE("gen-vel",     opts->bGenVel,  yesno_names);
 +    RTYPE ("gen-temp",    opts->tempi,    300.0);
 +    ITYPE ("gen-seed",    opts->seed,     -1);
 +
 +    /* Shake stuff */
 +    CCTYPE ("OPTIONS FOR BONDS");
 +    EETYPE("constraints", opts->nshake,   constraints);
 +    CTYPE ("Type of constraint algorithm");
 +    EETYPE("constraint-algorithm",  ir->eConstrAlg, econstr_names);
 +    CTYPE ("Do not constrain the start configuration");
 +    EETYPE("continuation", ir->bContinuation, yesno_names);
 +    CTYPE ("Use successive overrelaxation to reduce the number of shake iterations");
 +    EETYPE("Shake-SOR", ir->bShakeSOR, yesno_names);
 +    CTYPE ("Relative tolerance of shake");
 +    RTYPE ("shake-tol", ir->shake_tol, 0.0001);
 +    CTYPE ("Highest order in the expansion of the constraint coupling matrix");
 +    ITYPE ("lincs-order", ir->nProjOrder, 4);
 +    CTYPE ("Number of iterations in the final step of LINCS. 1 is fine for");
 +    CTYPE ("normal simulations, but use 2 to conserve energy in NVE runs.");
 +    CTYPE ("For energy minimization with constraints it should be 4 to 8.");
 +    ITYPE ("lincs-iter", ir->nLincsIter, 1);
 +    CTYPE ("Lincs will write a warning to the stderr if in one step a bond");
 +    CTYPE ("rotates over more degrees than");
 +    RTYPE ("lincs-warnangle", ir->LincsWarnAngle, 30.0);
 +    CTYPE ("Convert harmonic bonds to morse potentials");
 +    EETYPE("morse",       opts->bMorse, yesno_names);
 +
 +    /* Energy group exclusions */
 +    CCTYPE ("ENERGY GROUP EXCLUSIONS");
 +    CTYPE ("Pairs of energy groups for which all non-bonded interactions are excluded");
 +    STYPE ("energygrp-excl", is->egpexcl,     NULL);
 +
 +    /* Walls */
 +    CCTYPE ("WALLS");
 +    CTYPE ("Number of walls, type, atom types, densities and box-z scale factor for Ewald");
 +    ITYPE ("nwall", ir->nwall, 0);
 +    EETYPE("wall-type",     ir->wall_type,   ewt_names);
 +    RTYPE ("wall-r-linpot", ir->wall_r_linpot, -1);
 +    STYPE ("wall-atomtype", is->wall_atomtype, NULL);
 +    STYPE ("wall-density",  is->wall_density,  NULL);
 +    RTYPE ("wall-ewald-zfac", ir->wall_ewald_zfac, 3);
 +
 +    /* COM pulling */
 +    CCTYPE("COM PULLING");
 +    CTYPE("Pull type: no, umbrella, constraint or constant-force");
 +    EETYPE("pull",          ir->ePull, epull_names);
 +    if (ir->ePull != epullNO)
 +    {
 +        snew(ir->pull, 1);
 +        is->pull_grp = read_pullparams(&ninp, &inp, ir->pull, &opts->pull_start, wi);
 +    }
 +
 +    /* Enforced rotation */
 +    CCTYPE("ENFORCED ROTATION");
 +    CTYPE("Enforced rotation: No or Yes");
 +    EETYPE("rotation",       ir->bRot, yesno_names);
 +    if (ir->bRot)
 +    {
 +        snew(ir->rot, 1);
 +        is->rot_grp = read_rotparams(&ninp, &inp, ir->rot, wi);
 +    }
 +
 +    /* Interactive MD */
 +    ir->bIMD = FALSE;
 +    CCTYPE("Group to display and/or manipulate in interactive MD session");
 +    STYPE ("IMD-group", is->imd_grp, NULL);
 +    if (is->imd_grp[0] != '\0')
 +    {
 +        snew(ir->imd, 1);
 +        ir->bIMD = TRUE;
 +    }
 +
 +    /* Refinement */
 +    CCTYPE("NMR refinement stuff");
 +    CTYPE ("Distance restraints type: No, Simple or Ensemble");
 +    EETYPE("disre",       ir->eDisre,     edisre_names);
 +    CTYPE ("Force weighting of pairs in one distance restraint: Conservative or Equal");
 +    EETYPE("disre-weighting", ir->eDisreWeighting, edisreweighting_names);
 +    CTYPE ("Use sqrt of the time averaged times the instantaneous violation");
 +    EETYPE("disre-mixed", ir->bDisreMixed, yesno_names);
 +    RTYPE ("disre-fc",    ir->dr_fc,  1000.0);
 +    RTYPE ("disre-tau",   ir->dr_tau, 0.0);
 +    CTYPE ("Output frequency for pair distances to energy file");
 +    ITYPE ("nstdisreout", ir->nstdisreout, 100);
 +    CTYPE ("Orientation restraints: No or Yes");
 +    EETYPE("orire",       opts->bOrire,   yesno_names);
 +    CTYPE ("Orientation restraints force constant and tau for time averaging");
 +    RTYPE ("orire-fc",    ir->orires_fc,  0.0);
 +    RTYPE ("orire-tau",   ir->orires_tau, 0.0);
 +    STYPE ("orire-fitgrp", is->orirefitgrp,    NULL);
 +    CTYPE ("Output frequency for trace(SD) and S to energy file");
 +    ITYPE ("nstorireout", ir->nstorireout, 100);
 +
 +    /* free energy variables */
 +    CCTYPE ("Free energy variables");
 +    EETYPE("free-energy", ir->efep, efep_names);
 +    STYPE ("couple-moltype",  is->couple_moltype,  NULL);
 +    EETYPE("couple-lambda0", opts->couple_lam0, couple_lam);
 +    EETYPE("couple-lambda1", opts->couple_lam1, couple_lam);
 +    EETYPE("couple-intramol", opts->bCoupleIntra, yesno_names);
 +
 +    RTYPE ("init-lambda", fep->init_lambda, -1); /* start with -1 so
 +                                                    we can recognize if
 +                                                    it was not entered */
 +    ITYPE ("init-lambda-state", fep->init_fep_state, -1);
 +    RTYPE ("delta-lambda", fep->delta_lambda, 0.0);
 +    ITYPE ("nstdhdl", fep->nstdhdl, 50);
 +    STYPE ("fep-lambdas", is->fep_lambda[efptFEP], NULL);
 +    STYPE ("mass-lambdas", is->fep_lambda[efptMASS], NULL);
 +    STYPE ("coul-lambdas", is->fep_lambda[efptCOUL], NULL);
 +    STYPE ("vdw-lambdas", is->fep_lambda[efptVDW], NULL);
 +    STYPE ("bonded-lambdas", is->fep_lambda[efptBONDED], NULL);
 +    STYPE ("restraint-lambdas", is->fep_lambda[efptRESTRAINT], NULL);
 +    STYPE ("temperature-lambdas", is->fep_lambda[efptTEMPERATURE], NULL);
 +    ITYPE ("calc-lambda-neighbors", fep->lambda_neighbors, 1);
 +    STYPE ("init-lambda-weights", is->lambda_weights, NULL);
 +    EETYPE("dhdl-print-energy", fep->bPrintEnergy, yesno_names);
 +    RTYPE ("sc-alpha", fep->sc_alpha, 0.0);
 +    ITYPE ("sc-power", fep->sc_power, 1);
 +    RTYPE ("sc-r-power", fep->sc_r_power, 6.0);
 +    RTYPE ("sc-sigma", fep->sc_sigma, 0.3);
 +    EETYPE("sc-coul", fep->bScCoul, yesno_names);
 +    ITYPE ("dh_hist_size", fep->dh_hist_size, 0);
 +    RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1);
 +    EETYPE("separate-dhdl-file", fep->separate_dhdl_file,
 +           separate_dhdl_file_names);
 +    EETYPE("dhdl-derivatives", fep->dhdl_derivatives, dhdl_derivatives_names);
 +    ITYPE ("dh_hist_size", fep->dh_hist_size, 0);
 +    RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1);
 +
 +    /* Non-equilibrium MD stuff */
 +    CCTYPE("Non-equilibrium MD stuff");
 +    STYPE ("acc-grps",    is->accgrps,        NULL);
 +    STYPE ("accelerate",  is->acc,            NULL);
 +    STYPE ("freezegrps",  is->freeze,         NULL);
 +    STYPE ("freezedim",   is->frdim,          NULL);
 +    RTYPE ("cos-acceleration", ir->cos_accel, 0);
 +    STYPE ("deform",      is->deform,         NULL);
 +
 +    /* simulated tempering variables */
 +    CCTYPE("simulated tempering variables");
 +    EETYPE("simulated-tempering", ir->bSimTemp, yesno_names);
 +    EETYPE("simulated-tempering-scaling", ir->simtempvals->eSimTempScale, esimtemp_names);
 +    RTYPE("sim-temp-low", ir->simtempvals->simtemp_low, 300.0);
 +    RTYPE("sim-temp-high", ir->simtempvals->simtemp_high, 300.0);
 +
 +    /* expanded ensemble variables */
 +    if (ir->efep == efepEXPANDED || ir->bSimTemp)
 +    {
 +        read_expandedparams(&ninp, &inp, expand, wi);
 +    }
 +
 +    /* Electric fields */
 +    CCTYPE("Electric fields");
 +    CTYPE ("Format is number of terms (int) and for all terms an amplitude (real)");
 +    CTYPE ("and a phase angle (real)");
 +    STYPE ("E-x",     is->efield_x,   NULL);
 +    STYPE ("E-xt",    is->efield_xt,  NULL);
 +    STYPE ("E-y",     is->efield_y,   NULL);
 +    STYPE ("E-yt",    is->efield_yt,  NULL);
 +    STYPE ("E-z",     is->efield_z,   NULL);
 +    STYPE ("E-zt",    is->efield_zt,  NULL);
 +
 +    CCTYPE("Ion/water position swapping for computational electrophysiology setups");
 +    CTYPE("Swap positions along direction: no, X, Y, Z");
 +    EETYPE("swapcoords", ir->eSwapCoords, eSwapTypes_names);
 +    if (ir->eSwapCoords != eswapNO)
 +    {
 +        snew(ir->swap, 1);
 +        CTYPE("Swap attempt frequency");
 +        ITYPE("swap-frequency", ir->swap->nstswap, 1);
 +        CTYPE("Two index groups that contain the compartment-partitioning atoms");
 +        STYPE("split-group0", splitgrp0, NULL);
 +        STYPE("split-group1", splitgrp1, NULL);
 +        CTYPE("Use center of mass of split groups (yes/no), otherwise center of geometry is used");
 +        EETYPE("massw-split0", ir->swap->massw_split[0], yesno_names);
 +        EETYPE("massw-split1", ir->swap->massw_split[1], yesno_names);
 +
 +        CTYPE("Group name of ions that can be exchanged with solvent molecules");
 +        STYPE("swap-group", swapgrp, NULL);
 +        CTYPE("Group name of solvent molecules");
 +        STYPE("solvent-group", solgrp, NULL);
 +
 +        CTYPE("Split cylinder: radius, upper and lower extension (nm) (this will define the channels)");
 +        CTYPE("Note that the split cylinder settings do not have an influence on the swapping protocol,");
 +        CTYPE("however, if correctly defined, the ion permeation events are counted per channel");
 +        RTYPE("cyl0-r", ir->swap->cyl0r, 2.0);
 +        RTYPE("cyl0-up", ir->swap->cyl0u, 1.0);
 +        RTYPE("cyl0-down", ir->swap->cyl0l, 1.0);
 +        RTYPE("cyl1-r", ir->swap->cyl1r, 2.0);
 +        RTYPE("cyl1-up", ir->swap->cyl1u, 1.0);
 +        RTYPE("cyl1-down", ir->swap->cyl1l, 1.0);
 +
 +        CTYPE("Average the number of ions per compartment over these many swap attempt steps");
 +        ITYPE("coupl-steps", ir->swap->nAverage, 10);
 +        CTYPE("Requested number of anions and cations for each of the two compartments");
 +        CTYPE("-1 means fix the numbers as found in time step 0");
 +        ITYPE("anionsA", ir->swap->nanions[0], -1);
 +        ITYPE("cationsA", ir->swap->ncations[0], -1);
 +        ITYPE("anionsB", ir->swap->nanions[1], -1);
 +        ITYPE("cationsB", ir->swap->ncations[1], -1);
 +        CTYPE("Start to swap ions if threshold difference to requested count is reached");
 +        RTYPE("threshold", ir->swap->threshold, 1.0);
 +    }
 +
 +    /* AdResS defined thingies */
 +    CCTYPE ("AdResS parameters");
 +    EETYPE("adress",       ir->bAdress, yesno_names);
 +    if (ir->bAdress)
 +    {
 +        snew(ir->adress, 1);
 +        read_adressparams(&ninp, &inp, ir->adress, wi);
 +    }
 +
 +    /* User defined thingies */
 +    CCTYPE ("User defined thingies");
 +    STYPE ("user1-grps",  is->user1,          NULL);
 +    STYPE ("user2-grps",  is->user2,          NULL);
 +    ITYPE ("userint1",    ir->userint1,   0);
 +    ITYPE ("userint2",    ir->userint2,   0);
 +    ITYPE ("userint3",    ir->userint3,   0);
 +    ITYPE ("userint4",    ir->userint4,   0);
 +    RTYPE ("userreal1",   ir->userreal1,  0);
 +    RTYPE ("userreal2",   ir->userreal2,  0);
 +    RTYPE ("userreal3",   ir->userreal3,  0);
 +    RTYPE ("userreal4",   ir->userreal4,  0);
 +#undef CTYPE
 +
 +    write_inpfile(mdparout, ninp, inp, FALSE, wi);
 +    for (i = 0; (i < ninp); i++)
 +    {
 +        sfree(inp[i].name);
 +        sfree(inp[i].value);
 +    }
 +    sfree(inp);
 +
 +    /* Process options if necessary */
 +    for (m = 0; m < 2; m++)
 +    {
 +        for (i = 0; i < 2*DIM; i++)
 +        {
 +            dumdub[m][i] = 0.0;
 +        }
 +        if (ir->epc)
 +        {
 +            switch (ir->epct)
 +            {
 +                case epctISOTROPIC:
 +                    if (sscanf(dumstr[m], "%lf", &(dumdub[m][XX])) != 1)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 1)");
 +                    }
 +                    dumdub[m][YY] = dumdub[m][ZZ] = dumdub[m][XX];
 +                    break;
 +                case epctSEMIISOTROPIC:
 +                case epctSURFACETENSION:
 +                    if (sscanf(dumstr[m], "%lf%lf",
 +                               &(dumdub[m][XX]), &(dumdub[m][ZZ])) != 2)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 2)");
 +                    }
 +                    dumdub[m][YY] = dumdub[m][XX];
 +                    break;
 +                case epctANISOTROPIC:
 +                    if (sscanf(dumstr[m], "%lf%lf%lf%lf%lf%lf",
 +                               &(dumdub[m][XX]), &(dumdub[m][YY]), &(dumdub[m][ZZ]),
 +                               &(dumdub[m][3]), &(dumdub[m][4]), &(dumdub[m][5])) != 6)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 6)");
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Pressure coupling type %s not implemented yet",
 +                              epcoupltype_names[ir->epct]);
 +            }
 +        }
 +    }
 +    clear_mat(ir->ref_p);
 +    clear_mat(ir->compress);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        ir->ref_p[i][i]    = dumdub[1][i];
 +        ir->compress[i][i] = dumdub[0][i];
 +    }
 +    if (ir->epct == epctANISOTROPIC)
 +    {
 +        ir->ref_p[XX][YY] = dumdub[1][3];
 +        ir->ref_p[XX][ZZ] = dumdub[1][4];
 +        ir->ref_p[YY][ZZ] = dumdub[1][5];
 +        if (ir->ref_p[XX][YY] != 0 && ir->ref_p[XX][ZZ] != 0 && ir->ref_p[YY][ZZ] != 0)
 +        {
 +            warning(wi, "All off-diagonal reference pressures are non-zero. Are you sure you want to apply a threefold shear stress?\n");
 +        }
 +        ir->compress[XX][YY] = dumdub[0][3];
 +        ir->compress[XX][ZZ] = dumdub[0][4];
 +        ir->compress[YY][ZZ] = dumdub[0][5];
 +        for (i = 0; i < DIM; i++)
 +        {
 +            for (m = 0; m < i; m++)
 +            {
 +                ir->ref_p[i][m]    = ir->ref_p[m][i];
 +                ir->compress[i][m] = ir->compress[m][i];
 +            }
 +        }
 +    }
 +
 +    if (ir->comm_mode == ecmNO)
 +    {
 +        ir->nstcomm = 0;
 +    }
 +
 +    opts->couple_moltype = NULL;
 +    if (strlen(is->couple_moltype) > 0)
 +    {
 +        if (ir->efep != efepNO)
 +        {
 +            opts->couple_moltype = strdup(is->couple_moltype);
 +            if (opts->couple_lam0 == opts->couple_lam1)
 +            {
 +                warning(wi, "The lambda=0 and lambda=1 states for coupling are identical");
 +            }
 +            if (ir->eI == eiMD && (opts->couple_lam0 == ecouplamNONE ||
 +                                   opts->couple_lam1 == ecouplamNONE))
 +            {
 +                warning(wi, "For proper sampling of the (nearly) decoupled state, stochastic dynamics should be used");
 +            }
 +        }
 +        else
 +        {
 +            warning(wi, "Can not couple a molecule with free_energy = no");
 +        }
 +    }
 +    /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
 +    if (ir->efep != efepNO)
 +    {
 +        if (fep->delta_lambda > 0)
 +        {
 +            ir->efep = efepSLOWGROWTH;
 +        }
 +    }
 +
 +    if (ir->bSimTemp)
 +    {
 +        fep->bPrintEnergy = TRUE;
 +        /* always print out the energy to dhdl if we are doing expanded ensemble, since we need the total energy
 +           if the temperature is changing. */
 +    }
 +
 +    if ((ir->efep != efepNO) || ir->bSimTemp)
 +    {
 +        ir->bExpanded = FALSE;
 +        if ((ir->efep == efepEXPANDED) || ir->bSimTemp)
 +        {
 +            ir->bExpanded = TRUE;
 +        }
 +        do_fep_params(ir, is->fep_lambda, is->lambda_weights);
 +        if (ir->bSimTemp) /* done after fep params */
 +        {
 +            do_simtemp_params(ir);
 +        }
 +    }
 +    else
 +    {
 +        ir->fepvals->n_lambda = 0;
 +    }
 +
 +    /* WALL PARAMETERS */
 +
 +    do_wall_params(ir, is->wall_atomtype, is->wall_density, opts);
 +
 +    /* ORIENTATION RESTRAINT PARAMETERS */
 +
 +    if (opts->bOrire && str_nelem(is->orirefitgrp, MAXPTR, NULL) != 1)
 +    {
 +        warning_error(wi, "ERROR: Need one orientation restraint fit group\n");
 +    }
 +
 +    /* DEFORMATION PARAMETERS */
 +
 +    clear_mat(ir->deform);
 +    for (i = 0; i < 6; i++)
 +    {
 +        dumdub[0][i] = 0;
 +    }
 +    m = sscanf(is->deform, "%lf %lf %lf %lf %lf %lf",
 +               &(dumdub[0][0]), &(dumdub[0][1]), &(dumdub[0][2]),
 +               &(dumdub[0][3]), &(dumdub[0][4]), &(dumdub[0][5]));
 +    for (i = 0; i < 3; i++)
 +    {
 +        ir->deform[i][i] = dumdub[0][i];
 +    }
 +    ir->deform[YY][XX] = dumdub[0][3];
 +    ir->deform[ZZ][XX] = dumdub[0][4];
 +    ir->deform[ZZ][YY] = dumdub[0][5];
 +    if (ir->epc != epcNO)
 +    {
 +        for (i = 0; i < 3; i++)
 +        {
 +            for (j = 0; j <= i; j++)
 +            {
 +                if (ir->deform[i][j] != 0 && ir->compress[i][j] != 0)
 +                {
 +                    warning_error(wi, "A box element has deform set and compressibility > 0");
 +                }
 +            }
 +        }
 +        for (i = 0; i < 3; i++)
 +        {
 +            for (j = 0; j < i; j++)
 +            {
 +                if (ir->deform[i][j] != 0)
 +                {
 +                    for (m = j; m < DIM; m++)
 +                    {
 +                        if (ir->compress[m][j] != 0)
 +                        {
 +                            sprintf(warn_buf, "An off-diagonal box element has deform set while compressibility > 0 for the same component of another box vector, this might lead to spurious periodicity effects.");
 +                            warning(wi, warn_buf);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Ion/water position swapping checks */
 +    if (ir->eSwapCoords != eswapNO)
 +    {
 +        if (ir->swap->nstswap < 1)
 +        {
 +            warning_error(wi, "swap_frequency must be 1 or larger when ion swapping is requested");
 +        }
 +        if (ir->swap->nAverage < 1)
 +        {
 +            warning_error(wi, "coupl_steps must be 1 or larger.\n");
 +        }
 +        if (ir->swap->threshold < 1.0)
 +        {
 +            warning_error(wi, "Ion count threshold must be at least 1.\n");
 +        }
 +    }
 +
 +    sfree(dumstr[0]);
 +    sfree(dumstr[1]);
 +}
 +
 +static int search_QMstring(const char *s, int ng, const char *gn[])
 +{
 +    /* same as normal search_string, but this one searches QM strings */
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS, "this QM method or basisset (%s) is not implemented\n!", s);
 +
 +    return -1;
 +
 +} /* search_QMstring */
 +
 +/* We would like gn to be const as well, but C doesn't allow this */
 +int search_string(const char *s, int ng, char *gn[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS,
 +              "Group %s referenced in the .mdp file was not found in the index file.\n"
 +              "Group names must match either [moleculetype] names or custom index group\n"
 +              "names, in which case you must supply an index file to the '-n' option\n"
 +              "of grompp.",
 +              s);
 +
 +    return -1;
 +}
 +
 +static gmx_bool do_numbering(int natoms, gmx_groups_t *groups, int ng, char *ptrs[],
 +                             t_blocka *block, char *gnames[],
 +                             int gtype, int restnm,
 +                             int grptp, gmx_bool bVerbose,
 +                             warninp_t wi)
 +{
 +    unsigned short *cbuf;
 +    t_grps         *grps = &(groups->grps[gtype]);
 +    int             i, j, gid, aj, ognr, ntot = 0;
 +    const char     *title;
 +    gmx_bool        bRest;
 +    char            warn_buf[STRLEN];
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Starting numbering %d groups of type %d\n", ng, gtype);
 +    }
 +
 +    title = gtypes[gtype];
 +
 +    snew(cbuf, natoms);
 +    /* Mark all id's as not set */
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        cbuf[i] = NOGID;
 +    }
 +
 +    snew(grps->nm_ind, ng+1); /* +1 for possible rest group */
 +    for (i = 0; (i < ng); i++)
 +    {
 +        /* Lookup the group name in the block structure */
 +        gid = search_string(ptrs[i], block->nr, gnames);
 +        if ((grptp != egrptpONE) || (i == 0))
 +        {
 +            grps->nm_ind[grps->nr++] = gid;
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Found gid %d for group %s\n", gid, ptrs[i]);
 +        }
 +
 +        /* Now go over the atoms in the group */
 +        for (j = block->index[gid]; (j < block->index[gid+1]); j++)
 +        {
 +
 +            aj = block->a[j];
 +
 +            /* Range checking */
 +            if ((aj < 0) || (aj >= natoms))
 +            {
 +                gmx_fatal(FARGS, "Invalid atom number %d in indexfile", aj);
 +            }
 +            /* Lookup up the old group number */
 +            ognr = cbuf[aj];
 +            if (ognr != NOGID)
 +            {
 +                gmx_fatal(FARGS, "Atom %d in multiple %s groups (%d and %d)",
 +                          aj+1, title, ognr+1, i+1);
 +            }
 +            else
 +            {
 +                /* Store the group number in buffer */
 +                if (grptp == egrptpONE)
 +                {
 +                    cbuf[aj] = 0;
 +                }
 +                else
 +                {
 +                    cbuf[aj] = i;
 +                }
 +                ntot++;
 +            }
 +        }
 +    }
 +
 +    /* Now check whether we have done all atoms */
 +    bRest = FALSE;
 +    if (ntot != natoms)
 +    {
 +        if (grptp == egrptpALL)
 +        {
 +            gmx_fatal(FARGS, "%d atoms are not part of any of the %s groups",
 +                      natoms-ntot, title);
 +        }
 +        else if (grptp == egrptpPART)
 +        {
 +            sprintf(warn_buf, "%d atoms are not part of any of the %s groups",
 +                    natoms-ntot, title);
 +            warning_note(wi, warn_buf);
 +        }
 +        /* Assign all atoms currently unassigned to a rest group */
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            if (cbuf[j] == NOGID)
 +            {
 +                cbuf[j] = grps->nr;
 +                bRest   = TRUE;
 +            }
 +        }
 +        if (grptp != egrptpPART)
 +        {
 +            if (bVerbose)
 +            {
 +                fprintf(stderr,
 +                        "Making dummy/rest group for %s containing %d elements\n",
 +                        title, natoms-ntot);
 +            }
 +            /* Add group name "rest" */
 +            grps->nm_ind[grps->nr] = restnm;
 +
 +            /* Assign the rest name to all atoms not currently assigned to a group */
 +            for (j = 0; (j < natoms); j++)
 +            {
 +                if (cbuf[j] == NOGID)
 +                {
 +                    cbuf[j] = grps->nr;
 +                }
 +            }
 +            grps->nr++;
 +        }
 +    }
 +
 +    if (grps->nr == 1 && (ntot == 0 || ntot == natoms))
 +    {
 +        /* All atoms are part of one (or no) group, no index required */
 +        groups->ngrpnr[gtype] = 0;
 +        groups->grpnr[gtype]  = NULL;
 +    }
 +    else
 +    {
 +        groups->ngrpnr[gtype] = natoms;
 +        snew(groups->grpnr[gtype], natoms);
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            groups->grpnr[gtype][j] = cbuf[j];
 +        }
 +    }
 +
 +    sfree(cbuf);
 +
 +    return (bRest && grptp == egrptpPART);
 +}
 +
 +static void calc_nrdf(gmx_mtop_t *mtop, t_inputrec *ir, char **gnames)
 +{
 +    t_grpopts              *opts;
 +    gmx_groups_t           *groups;
 +    t_pull                 *pull;
 +    int                     natoms, ai, aj, i, j, d, g, imin, jmin;
 +    t_iatom                *ia;
 +    int                    *nrdf2, *na_vcm, na_tot;
 +    double                 *nrdf_tc, *nrdf_vcm, nrdf_uc, n_sub = 0;
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     mb, mol, ftype, as;
 +    gmx_molblock_t         *molb;
 +    gmx_moltype_t          *molt;
 +
 +    /* Calculate nrdf.
 +     * First calc 3xnr-atoms for each group
 +     * then subtract half a degree of freedom for each constraint
 +     *
 +     * Only atoms and nuclei contribute to the degrees of freedom...
 +     */
 +
 +    opts = &ir->opts;
 +
 +    groups = &mtop->groups;
 +    natoms = mtop->natoms;
 +
 +    /* Allocate one more for a possible rest group */
 +    /* We need to sum degrees of freedom into doubles,
 +     * since floats give too low nrdf's above 3 million atoms.
 +     */
 +    snew(nrdf_tc, groups->grps[egcTC].nr+1);
 +    snew(nrdf_vcm, groups->grps[egcVCM].nr+1);
 +    snew(na_vcm, groups->grps[egcVCM].nr+1);
 +
 +    for (i = 0; i < groups->grps[egcTC].nr; i++)
 +    {
 +        nrdf_tc[i] = 0;
 +    }
 +    for (i = 0; i < groups->grps[egcVCM].nr+1; i++)
 +    {
 +        nrdf_vcm[i] = 0;
 +    }
 +
 +    snew(nrdf2, natoms);
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +    {
 +        nrdf2[i] = 0;
 +        if (atom->ptype == eptAtom || atom->ptype == eptNucleus)
 +        {
 +            g = ggrpnr(groups, egcFREEZE, i);
 +            /* Double count nrdf for particle i */
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (opts->nFreeze[g][d] == 0)
 +                {
 +                    nrdf2[i] += 2;
 +                }
 +            }
 +            nrdf_tc [ggrpnr(groups, egcTC, i)]  += 0.5*nrdf2[i];
 +            nrdf_vcm[ggrpnr(groups, egcVCM, i)] += 0.5*nrdf2[i];
 +        }
 +    }
 +
 +    as = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        molt = &mtop->moltype[molb->type];
 +        atom = molt->atoms.atom;
 +        for (mol = 0; mol < molb->nmol; mol++)
 +        {
 +            for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
 +            {
 +                ia = molt->ilist[ftype].iatoms;
 +                for (i = 0; i < molt->ilist[ftype].nr; )
 +                {
 +                    /* Subtract degrees of freedom for the constraints,
 +                     * if the particles still have degrees of freedom left.
 +                     * If one of the particles is a vsite or a shell, then all
 +                     * constraint motion will go there, but since they do not
 +                     * contribute to the constraints the degrees of freedom do not
 +                     * change.
 +                     */
 +                    ai = as + ia[1];
 +                    aj = as + ia[2];
 +                    if (((atom[ia[1]].ptype == eptNucleus) ||
 +                         (atom[ia[1]].ptype == eptAtom)) &&
 +                        ((atom[ia[2]].ptype == eptNucleus) ||
 +                         (atom[ia[2]].ptype == eptAtom)))
 +                    {
 +                        if (nrdf2[ai] > 0)
 +                        {
 +                            jmin = 1;
 +                        }
 +                        else
 +                        {
 +                            jmin = 2;
 +                        }
 +                        if (nrdf2[aj] > 0)
 +                        {
 +                            imin = 1;
 +                        }
 +                        else
 +                        {
 +                            imin = 2;
 +                        }
 +                        imin       = min(imin, nrdf2[ai]);
 +                        jmin       = min(jmin, nrdf2[aj]);
 +                        nrdf2[ai] -= imin;
 +                        nrdf2[aj] -= jmin;
 +                        nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                        nrdf_tc [ggrpnr(groups, egcTC, aj)]  -= 0.5*jmin;
 +                        nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                        nrdf_vcm[ggrpnr(groups, egcVCM, aj)] -= 0.5*jmin;
 +                    }
 +                    ia += interaction_function[ftype].nratoms+1;
 +                    i  += interaction_function[ftype].nratoms+1;
 +                }
 +            }
 +            ia = molt->ilist[F_SETTLE].iatoms;
 +            for (i = 0; i < molt->ilist[F_SETTLE].nr; )
 +            {
 +                /* Subtract 1 dof from every atom in the SETTLE */
 +                for (j = 0; j < 3; j++)
 +                {
 +                    ai         = as + ia[1+j];
 +                    imin       = min(2, nrdf2[ai]);
 +                    nrdf2[ai] -= imin;
 +                    nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                    nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                }
 +                ia += 4;
 +                i  += 4;
 +            }
 +            as += molt->atoms.nr;
 +        }
 +    }
 +
 +    if (ir->ePull == epullCONSTRAINT)
 +    {
 +        /* Correct nrdf for the COM constraints.
 +         * We correct using the TC and VCM group of the first atom
 +         * in the reference and pull group. If atoms in one pull group
 +         * belong to different TC or VCM groups it is anyhow difficult
 +         * to determine the optimal nrdf assignment.
 +         */
 +        pull = ir->pull;
 +
 +        for (i = 0; i < pull->ncoord; i++)
 +        {
 +            imin = 1;
 +
 +            for (j = 0; j < 2; j++)
 +            {
 +                const t_pull_group *pgrp;
 +
 +                pgrp = &pull->group[pull->coord[i].group[j]];
 +
 +                if (pgrp->nat > 0)
 +                {
 +                    /* Subtract 1/2 dof from each group */
 +                    ai = pgrp->ind[0];
 +                    nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                    nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                    if (nrdf_tc[ggrpnr(groups, egcTC, ai)] < 0)
 +                    {
 +                        gmx_fatal(FARGS, "Center of mass pulling constraints caused the number of degrees of freedom for temperature coupling group %s to be negative", gnames[groups->grps[egcTC].nm_ind[ggrpnr(groups, egcTC, ai)]]);
 +                    }
 +                }
 +                else
 +                {
 +                    /* We need to subtract the whole DOF from group j=1 */
 +                    imin += 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->nstcomm != 0)
 +    {
 +        /* Subtract 3 from the number of degrees of freedom in each vcm group
 +         * when com translation is removed and 6 when rotation is removed
 +         * as well.
 +         */
 +        switch (ir->comm_mode)
 +        {
 +            case ecmLINEAR:
 +                n_sub = ndof_com(ir);
 +                break;
 +            case ecmANGULAR:
 +                n_sub = 6;
 +                break;
 +            default:
 +                n_sub = 0;
 +                gmx_incons("Checking comm_mode");
 +        }
 +
 +        for (i = 0; i < groups->grps[egcTC].nr; i++)
 +        {
 +            /* Count the number of atoms of TC group i for every VCM group */
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
 +                na_vcm[j] = 0;
 +            }
 +            na_tot = 0;
 +            for (ai = 0; ai < natoms; ai++)
 +            {
 +                if (ggrpnr(groups, egcTC, ai) == i)
 +                {
 +                    na_vcm[ggrpnr(groups, egcVCM, ai)]++;
 +                    na_tot++;
 +                }
 +            }
 +            /* Correct for VCM removal according to the fraction of each VCM
 +             * group present in this TC group.
 +             */
 +            nrdf_uc = nrdf_tc[i];
 +            if (debug)
 +            {
 +                fprintf(debug, "T-group[%d] nrdf_uc = %g, n_sub = %g\n",
 +                        i, nrdf_uc, n_sub);
 +            }
 +            nrdf_tc[i] = 0;
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
 +                if (nrdf_vcm[j] > n_sub)
 +                {
 +                    nrdf_tc[i] += nrdf_uc*((double)na_vcm[j]/(double)na_tot)*
 +                        (nrdf_vcm[j] - n_sub)/nrdf_vcm[j];
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "  nrdf_vcm[%d] = %g, nrdf = %g\n",
 +                            j, nrdf_vcm[j], nrdf_tc[i]);
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; (i < groups->grps[egcTC].nr); i++)
 +    {
 +        opts->nrdf[i] = nrdf_tc[i];
 +        if (opts->nrdf[i] < 0)
 +        {
 +            opts->nrdf[i] = 0;
 +        }
 +        fprintf(stderr,
 +                "Number of degrees of freedom in T-Coupling group %s is %.2f\n",
 +                gnames[groups->grps[egcTC].nm_ind[i]], opts->nrdf[i]);
 +    }
 +
 +    sfree(nrdf2);
 +    sfree(nrdf_tc);
 +    sfree(nrdf_vcm);
 +    sfree(na_vcm);
 +}
 +
 +static void decode_cos(char *s, t_cosines *cosine)
 +{
 +    char   *t;
 +    char    format[STRLEN], f1[STRLEN];
 +    double  a, phi;
 +    int     i;
 +
 +    t = strdup(s);
 +    trim(t);
 +
 +    cosine->n   = 0;
 +    cosine->a   = NULL;
 +    cosine->phi = NULL;
 +    if (strlen(t))
 +    {
 +        sscanf(t, "%d", &(cosine->n));
 +        if (cosine->n <= 0)
 +        {
 +            cosine->n = 0;
 +        }
 +        else
 +        {
 +            snew(cosine->a, cosine->n);
 +            snew(cosine->phi, cosine->n);
 +
 +            sprintf(format, "%%*d");
 +            for (i = 0; (i < cosine->n); i++)
 +            {
 +                strcpy(f1, format);
 +                strcat(f1, "%lf%lf");
 +                if (sscanf(t, f1, &a, &phi) < 2)
 +                {
 +                    gmx_fatal(FARGS, "Invalid input for electric field shift: '%s'", t);
 +                }
 +                cosine->a[i]   = a;
 +                cosine->phi[i] = phi;
 +                strcat(format, "%*lf%*lf");
 +            }
 +        }
 +    }
 +    sfree(t);
 +}
 +
 +static gmx_bool do_egp_flag(t_inputrec *ir, gmx_groups_t *groups,
 +                            const char *option, const char *val, int flag)
 +{
 +    /* The maximum number of energy group pairs would be MAXPTR*(MAXPTR+1)/2.
 +     * But since this is much larger than STRLEN, such a line can not be parsed.
 +     * The real maximum is the number of names that fit in a string: STRLEN/2.
 +     */
 +#define EGP_MAX (STRLEN/2)
 +    int      nelem, i, j, k, nr;
 +    char    *names[EGP_MAX];
 +    char  ***gnames;
 +    gmx_bool bSet;
 +
 +    gnames = groups->grpname;
 +
 +    nelem = str_nelem(val, EGP_MAX, names);
 +    if (nelem % 2 != 0)
 +    {
 +        gmx_fatal(FARGS, "The number of groups for %s is odd", option);
 +    }
 +    nr   = groups->grps[egcENER].nr;
 +    bSet = FALSE;
 +    for (i = 0; i < nelem/2; i++)
 +    {
 +        j = 0;
 +        while ((j < nr) &&
 +               gmx_strcasecmp(names[2*i], *(gnames[groups->grps[egcENER].nm_ind[j]])))
 +        {
 +            j++;
 +        }
 +        if (j == nr)
 +        {
 +            gmx_fatal(FARGS, "%s in %s is not an energy group\n",
 +                      names[2*i], option);
 +        }
 +        k = 0;
 +        while ((k < nr) &&
 +               gmx_strcasecmp(names[2*i+1], *(gnames[groups->grps[egcENER].nm_ind[k]])))
 +        {
 +            k++;
 +        }
 +        if (k == nr)
 +        {
 +            gmx_fatal(FARGS, "%s in %s is not an energy group\n",
 +                      names[2*i+1], option);
 +        }
 +        if ((j < nr) && (k < nr))
 +        {
 +            ir->opts.egp_flags[nr*j+k] |= flag;
 +            ir->opts.egp_flags[nr*k+j] |= flag;
 +            bSet = TRUE;
 +        }
 +    }
 +
 +    return bSet;
 +}
 +
 +
 +static void make_swap_groups(
 +        t_swapcoords *swap,
 +        char         *swapgname,
 +        char         *splitg0name,
 +        char         *splitg1name,
 +        char         *solgname,
 +        t_blocka     *grps,
 +        char        **gnames)
 +{
 +    int   ig = -1, i = 0, j;
 +    char *splitg;
 +
 +
 +    /* Just a quick check here, more thorough checks are in mdrun */
 +    if (strcmp(splitg0name, splitg1name) == 0)
 +    {
 +        gmx_fatal(FARGS, "The split groups can not both be '%s'.", splitg0name);
 +    }
 +
 +    /* First get the swap group index atoms */
 +    ig        = search_string(swapgname, grps->nr, gnames);
 +    swap->nat = grps->index[ig+1] - grps->index[ig];
 +    if (swap->nat > 0)
 +    {
 +        fprintf(stderr, "Swap group '%s' contains %d atoms.\n", swapgname, swap->nat);
 +        snew(swap->ind, swap->nat);
 +        for (i = 0; i < swap->nat; i++)
 +        {
 +            swap->ind[i] = grps->a[grps->index[ig]+i];
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "You defined an empty group of atoms for swapping.");
 +    }
 +
 +    /* Now do so for the split groups */
 +    for (j = 0; j < 2; j++)
 +    {
 +        if (j == 0)
 +        {
 +            splitg = splitg0name;
 +        }
 +        else
 +        {
 +            splitg = splitg1name;
 +        }
 +
 +        ig                 = search_string(splitg, grps->nr, gnames);
 +        swap->nat_split[j] = grps->index[ig+1] - grps->index[ig];
 +        if (swap->nat_split[j] > 0)
 +        {
 +            fprintf(stderr, "Split group %d '%s' contains %d atom%s.\n",
 +                    j, splitg, swap->nat_split[j], (swap->nat_split[j] > 1) ? "s" : "");
 +            snew(swap->ind_split[j], swap->nat_split[j]);
 +            for (i = 0; i < swap->nat_split[j]; i++)
 +            {
 +                swap->ind_split[j][i] = grps->a[grps->index[ig]+i];
 +            }
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Split group %d has to contain at least 1 atom!", j);
 +        }
 +    }
 +
 +    /* Now get the solvent group index atoms */
 +    ig            = search_string(solgname, grps->nr, gnames);
 +    swap->nat_sol = grps->index[ig+1] - grps->index[ig];
 +    if (swap->nat_sol > 0)
 +    {
 +        fprintf(stderr, "Solvent group '%s' contains %d atoms.\n", solgname, swap->nat_sol);
 +        snew(swap->ind_sol, swap->nat_sol);
 +        for (i = 0; i < swap->nat_sol; i++)
 +        {
 +            swap->ind_sol[i] = grps->a[grps->index[ig]+i];
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "You defined an empty group of solvent. Cannot exchange ions.");
 +    }
 +}
 +
 +
 +void make_IMD_group(t_IMD *IMDgroup, char *IMDgname, t_blocka *grps, char **gnames)
 +{
 +    int      ig = -1, i;
 +
 +
 +    ig            = search_string(IMDgname, grps->nr, gnames);
 +    IMDgroup->nat = grps->index[ig+1] - grps->index[ig];
 +
 +    if (IMDgroup->nat > 0)
 +    {
 +        fprintf(stderr, "Group '%s' with %d atoms can be activated for interactive molecular dynamics (IMD).\n",
 +                IMDgname, IMDgroup->nat);
 +        snew(IMDgroup->ind, IMDgroup->nat);
 +        for (i = 0; i < IMDgroup->nat; i++)
 +        {
 +            IMDgroup->ind[i] = grps->a[grps->index[ig]+i];
 +        }
 +    }
 +}
 +
 +
 +void do_index(const char* mdparin, const char *ndx,
 +              gmx_mtop_t *mtop,
 +              gmx_bool bVerbose,
 +              t_inputrec *ir, rvec *v,
 +              warninp_t wi)
 +{
 +    t_blocka     *grps;
 +    gmx_groups_t *groups;
 +    int           natoms;
 +    t_symtab     *symtab;
 +    t_atoms       atoms_all;
 +    char          warnbuf[STRLEN], **gnames;
 +    int           nr, ntcg, ntau_t, nref_t, nacc, nofg, nSA, nSA_points, nSA_time, nSA_temp;
 +    real          tau_min;
 +    int           nstcmin;
 +    int           nacg, nfreeze, nfrdim, nenergy, nvcm, nuser;
 +    char         *ptr1[MAXPTR], *ptr2[MAXPTR], *ptr3[MAXPTR];
 +    int           i, j, k, restnm;
 +    real          SAtime;
 +    gmx_bool      bExcl, bTable, bSetTCpar, bAnneal, bRest;
 +    int           nQMmethod, nQMbasis, nQMcharge, nQMmult, nbSH, nCASorb, nCASelec,
 +                  nSAon, nSAoff, nSAsteps, nQMg, nbOPT, nbTS;
 +    char          warn_buf[STRLEN];
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing index file...\n");
 +    }
 +    debug_gmx();
 +    if (ndx == NULL)
 +    {
 +        snew(grps, 1);
 +        snew(grps->index, 1);
 +        snew(gnames, 1);
 +        atoms_all = gmx_mtop_global_atoms(mtop);
 +        analyse(&atoms_all, grps, &gnames, FALSE, TRUE);
 +        free_t_atoms(&atoms_all, FALSE);
 +    }
 +    else
 +    {
 +        grps = init_index(ndx, &gnames);
 +    }
 +
 +    groups = &mtop->groups;
 +    natoms = mtop->natoms;
 +    symtab = &mtop->symtab;
 +
 +    snew(groups->grpname, grps->nr+1);
 +
 +    for (i = 0; (i < grps->nr); i++)
 +    {
 +        groups->grpname[i] = put_symtab(symtab, gnames[i]);
 +    }
 +    groups->grpname[i] = put_symtab(symtab, "rest");
 +    restnm             = i;
 +    srenew(gnames, grps->nr+1);
 +    gnames[restnm]   = *(groups->grpname[i]);
 +    groups->ngrpname = grps->nr+1;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    ntau_t = str_nelem(is->tau_t, MAXPTR, ptr1);
 +    nref_t = str_nelem(is->ref_t, MAXPTR, ptr2);
 +    ntcg   = str_nelem(is->tcgrps, MAXPTR, ptr3);
 +    if ((ntau_t != ntcg) || (nref_t != ntcg))
 +    {
 +        gmx_fatal(FARGS, "Invalid T coupling input: %d groups, %d ref-t values and "
 +                  "%d tau-t values", ntcg, nref_t, ntau_t);
 +    }
 +
 +    bSetTCpar = (ir->etc || EI_SD(ir->eI) || ir->eI == eiBD || EI_TPI(ir->eI));
 +    do_numbering(natoms, groups, ntcg, ptr3, grps, gnames, egcTC,
 +                 restnm, bSetTCpar ? egrptpALL : egrptpALL_GENREST, bVerbose, wi);
 +    nr            = groups->grps[egcTC].nr;
 +    ir->opts.ngtc = nr;
 +    snew(ir->opts.nrdf, nr);
 +    snew(ir->opts.tau_t, nr);
 +    snew(ir->opts.ref_t, nr);
 +    if (ir->eI == eiBD && ir->bd_fric == 0)
 +    {
 +        fprintf(stderr, "bd-fric=0, so tau-t will be used as the inverse friction constant(s)\n");
 +    }
 +
 +    if (bSetTCpar)
 +    {
 +        if (nr != nref_t)
 +        {
 +            gmx_fatal(FARGS, "Not enough ref-t and tau-t values!");
 +        }
 +
 +        tau_min = 1e20;
 +        for (i = 0; (i < nr); i++)
 +        {
 +            ir->opts.tau_t[i] = strtod(ptr1[i], NULL);
 +            if ((ir->eI == eiBD || ir->eI == eiSD2) && ir->opts.tau_t[i] <= 0)
 +            {
 +                sprintf(warn_buf, "With integrator %s tau-t should be larger than 0", ei_names[ir->eI]);
 +                warning_error(wi, warn_buf);
 +            }
 +
 +            if (ir->etc != etcVRESCALE && ir->opts.tau_t[i] == 0)
 +            {
 +                warning_note(wi, "tau-t = -1 is the value to signal that a group should not have temperature coupling. Treating your use of tau-t = 0 as if you used -1.");
 +            }
 +
 +            if (ir->opts.tau_t[i] >= 0)
 +            {
 +                tau_min = min(tau_min, ir->opts.tau_t[i]);
 +            }
 +        }
 +        if (ir->etc != etcNO && ir->nsttcouple == -1)
 +        {
 +            ir->nsttcouple = ir_optimal_nsttcouple(ir);
 +        }
 +
 +        if (EI_VV(ir->eI))
 +        {
 +            if ((ir->etc == etcNOSEHOOVER) && (ir->epc == epcBERENDSEN))
 +            {
 +                gmx_fatal(FARGS, "Cannot do Nose-Hoover temperature with Berendsen pressure control with md-vv; use either vrescale temperature with berendsen pressure or Nose-Hoover temperature with MTTK pressure");
 +            }
 +            if ((ir->epc == epcMTTK) && (ir->etc > etcNO))
 +            {
 +                if (ir->nstpcouple != ir->nsttcouple)
 +                {
 +                    int mincouple = min(ir->nstpcouple, ir->nsttcouple);
 +                    ir->nstpcouple = ir->nsttcouple = mincouple;
 +                    sprintf(warn_buf, "for current Trotter decomposition methods with vv, nsttcouple and nstpcouple must be equal.  Both have been reset to min(nsttcouple,nstpcouple) = %d", mincouple);
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +        /* velocity verlet with averaged kinetic energy KE = 0.5*(v(t+1/2) - v(t-1/2)) is implemented
 +           primarily for testing purposes, and does not work with temperature coupling other than 1 */
 +
 +        if (ETC_ANDERSEN(ir->etc))
 +        {
 +            if (ir->nsttcouple != 1)
 +            {
 +                ir->nsttcouple = 1;
 +                sprintf(warn_buf, "Andersen temperature control methods assume nsttcouple = 1; there is no need for larger nsttcouple > 1, since no global parameters are computed. nsttcouple has been reset to 1");
 +                warning_note(wi, warn_buf);
 +            }
 +        }
 +        nstcmin = tcouple_min_integration_steps(ir->etc);
 +        if (nstcmin > 1)
 +        {
 +            if (tau_min/(ir->delta_t*ir->nsttcouple) < nstcmin)
 +            {
 +                sprintf(warn_buf, "For proper integration of the %s thermostat, tau-t (%g) should be at least %d times larger than nsttcouple*dt (%g)",
 +                        ETCOUPLTYPE(ir->etc),
 +                        tau_min, nstcmin,
 +                        ir->nsttcouple*ir->delta_t);
 +                warning(wi, warn_buf);
 +            }
 +        }
 +        for (i = 0; (i < nr); i++)
 +        {
 +            ir->opts.ref_t[i] = strtod(ptr2[i], NULL);
 +            if (ir->opts.ref_t[i] < 0)
 +            {
 +                gmx_fatal(FARGS, "ref-t for group %d negative", i);
 +            }
 +        }
 +        /* set the lambda mc temperature to the md integrator temperature (which should be defined
 +           if we are in this conditional) if mc_temp is negative */
 +        if (ir->expandedvals->mc_temp < 0)
 +        {
 +            ir->expandedvals->mc_temp = ir->opts.ref_t[0]; /*for now, set to the first reft */
 +        }
 +    }
 +
 +    /* Simulated annealing for each group. There are nr groups */
 +    nSA = str_nelem(is->anneal, MAXPTR, ptr1);
 +    if (nSA == 1 && (ptr1[0][0] == 'n' || ptr1[0][0] == 'N'))
 +    {
 +        nSA = 0;
 +    }
 +    if (nSA > 0 && nSA != nr)
 +    {
 +        gmx_fatal(FARGS, "Not enough annealing values: %d (for %d groups)\n", nSA, nr);
 +    }
 +    else
 +    {
 +        snew(ir->opts.annealing, nr);
 +        snew(ir->opts.anneal_npoints, nr);
 +        snew(ir->opts.anneal_time, nr);
 +        snew(ir->opts.anneal_temp, nr);
 +        for (i = 0; i < nr; i++)
 +        {
 +            ir->opts.annealing[i]      = eannNO;
 +            ir->opts.anneal_npoints[i] = 0;
 +            ir->opts.anneal_time[i]    = NULL;
 +            ir->opts.anneal_temp[i]    = NULL;
 +        }
 +        if (nSA > 0)
 +        {
 +            bAnneal = FALSE;
 +            for (i = 0; i < nr; i++)
 +            {
 +                if (ptr1[i][0] == 'n' || ptr1[i][0] == 'N')
 +                {
 +                    ir->opts.annealing[i] = eannNO;
 +                }
 +                else if (ptr1[i][0] == 's' || ptr1[i][0] == 'S')
 +                {
 +                    ir->opts.annealing[i] = eannSINGLE;
 +                    bAnneal               = TRUE;
 +                }
 +                else if (ptr1[i][0] == 'p' || ptr1[i][0] == 'P')
 +                {
 +                    ir->opts.annealing[i] = eannPERIODIC;
 +                    bAnneal               = TRUE;
 +                }
 +            }
 +            if (bAnneal)
 +            {
 +                /* Read the other fields too */
 +                nSA_points = str_nelem(is->anneal_npoints, MAXPTR, ptr1);
 +                if (nSA_points != nSA)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-npoints values for %d groups\n", nSA_points, nSA);
 +                }
 +                for (k = 0, i = 0; i < nr; i++)
 +                {
 +                    ir->opts.anneal_npoints[i] = strtol(ptr1[i], NULL, 10);
 +                    if (ir->opts.anneal_npoints[i] == 1)
 +                    {
 +                        gmx_fatal(FARGS, "Please specify at least a start and an end point for annealing\n");
 +                    }
 +                    snew(ir->opts.anneal_time[i], ir->opts.anneal_npoints[i]);
 +                    snew(ir->opts.anneal_temp[i], ir->opts.anneal_npoints[i]);
 +                    k += ir->opts.anneal_npoints[i];
 +                }
 +
 +                nSA_time = str_nelem(is->anneal_time, MAXPTR, ptr1);
 +                if (nSA_time != k)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-time values, wanter %d\n", nSA_time, k);
 +                }
 +                nSA_temp = str_nelem(is->anneal_temp, MAXPTR, ptr2);
 +                if (nSA_temp != k)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-temp values, wanted %d\n", nSA_temp, k);
 +                }
 +
 +                for (i = 0, k = 0; i < nr; i++)
 +                {
 +
 +                    for (j = 0; j < ir->opts.anneal_npoints[i]; j++)
 +                    {
 +                        ir->opts.anneal_time[i][j] = strtod(ptr1[k], NULL);
 +                        ir->opts.anneal_temp[i][j] = strtod(ptr2[k], NULL);
 +                        if (j == 0)
 +                        {
 +                            if (ir->opts.anneal_time[i][0] > (ir->init_t+GMX_REAL_EPS))
 +                            {
 +                                gmx_fatal(FARGS, "First time point for annealing > init_t.\n");
 +                            }
 +                        }
 +                        else
 +                        {
 +                            /* j>0 */
 +                            if (ir->opts.anneal_time[i][j] < ir->opts.anneal_time[i][j-1])
 +                            {
 +                                gmx_fatal(FARGS, "Annealing timepoints out of order: t=%f comes after t=%f\n",
 +                                          ir->opts.anneal_time[i][j], ir->opts.anneal_time[i][j-1]);
 +                            }
 +                        }
 +                        if (ir->opts.anneal_temp[i][j] < 0)
 +                        {
 +                            gmx_fatal(FARGS, "Found negative temperature in annealing: %f\n", ir->opts.anneal_temp[i][j]);
 +                        }
 +                        k++;
 +                    }
 +                }
 +                /* Print out some summary information, to make sure we got it right */
 +                for (i = 0, k = 0; i < nr; i++)
 +                {
 +                    if (ir->opts.annealing[i] != eannNO)
 +                    {
 +                        j = groups->grps[egcTC].nm_ind[i];
 +                        fprintf(stderr, "Simulated annealing for group %s: %s, %d timepoints\n",
 +                                *(groups->grpname[j]), eann_names[ir->opts.annealing[i]],
 +                                ir->opts.anneal_npoints[i]);
 +                        fprintf(stderr, "Time (ps)   Temperature (K)\n");
 +                        /* All terms except the last one */
 +                        for (j = 0; j < (ir->opts.anneal_npoints[i]-1); j++)
 +                        {
 +                            fprintf(stderr, "%9.1f      %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                        }
 +
 +                        /* Finally the last one */
 +                        j = ir->opts.anneal_npoints[i]-1;
 +                        if (ir->opts.annealing[i] == eannSINGLE)
 +                        {
 +                            fprintf(stderr, "%9.1f-     %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "%9.1f      %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                            if (fabs(ir->opts.anneal_temp[i][j]-ir->opts.anneal_temp[i][0]) > GMX_REAL_EPS)
 +                            {
 +                                warning_note(wi, "There is a temperature jump when your annealing loops back.\n");
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        make_pull_groups(ir->pull, is->pull_grp, grps, gnames);
 +
 +        make_pull_coords(ir->pull);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        make_rotation_groups(ir->rot, is->rot_grp, grps, gnames);
 +    }
 +
 +    if (ir->eSwapCoords != eswapNO)
 +    {
 +        make_swap_groups(ir->swap, swapgrp, splitgrp0, splitgrp1, solgrp, grps, gnames);
 +    }
 +
 +    /* Make indices for IMD session */
 +    if (ir->bIMD)
 +    {
 +        make_IMD_group(ir->imd, is->imd_grp, grps, gnames);
 +    }
 +
 +    nacc = str_nelem(is->acc, MAXPTR, ptr1);
 +    nacg = str_nelem(is->accgrps, MAXPTR, ptr2);
 +    if (nacg*DIM != nacc)
 +    {
 +        gmx_fatal(FARGS, "Invalid Acceleration input: %d groups and %d acc. values",
 +                  nacg, nacc);
 +    }
 +    do_numbering(natoms, groups, nacg, ptr2, grps, gnames, egcACC,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr = groups->grps[egcACC].nr;
 +    snew(ir->opts.acc, nr);
 +    ir->opts.ngacc = nr;
 +
 +    for (i = k = 0; (i < nacg); i++)
 +    {
 +        for (j = 0; (j < DIM); j++, k++)
 +        {
 +            ir->opts.acc[i][j] = strtod(ptr1[k], NULL);
 +        }
 +    }
 +    for (; (i < nr); i++)
 +    {
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            ir->opts.acc[i][j] = 0;
 +        }
 +    }
 +
 +    nfrdim  = str_nelem(is->frdim, MAXPTR, ptr1);
 +    nfreeze = str_nelem(is->freeze, MAXPTR, ptr2);
 +    if (nfrdim != DIM*nfreeze)
 +    {
 +        gmx_fatal(FARGS, "Invalid Freezing input: %d groups and %d freeze values",
 +                  nfreeze, nfrdim);
 +    }
 +    do_numbering(natoms, groups, nfreeze, ptr2, grps, gnames, egcFREEZE,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr             = groups->grps[egcFREEZE].nr;
 +    ir->opts.ngfrz = nr;
 +    snew(ir->opts.nFreeze, nr);
 +    for (i = k = 0; (i < nfreeze); i++)
 +    {
 +        for (j = 0; (j < DIM); j++, k++)
 +        {
 +            ir->opts.nFreeze[i][j] = (gmx_strncasecmp(ptr1[k], "Y", 1) == 0);
 +            if (!ir->opts.nFreeze[i][j])
 +            {
 +                if (gmx_strncasecmp(ptr1[k], "N", 1) != 0)
 +                {
 +                    sprintf(warnbuf, "Please use Y(ES) or N(O) for freezedim only "
 +                            "(not %s)", ptr1[k]);
 +                    warning(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +    for (; (i < nr); i++)
 +    {
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            ir->opts.nFreeze[i][j] = 0;
 +        }
 +    }
 +
 +    nenergy = str_nelem(is->energy, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nenergy, ptr1, grps, gnames, egcENER,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    add_wall_energrps(groups, ir->nwall, symtab);
 +    ir->opts.ngener = groups->grps[egcENER].nr;
 +    nvcm            = str_nelem(is->vcm, MAXPTR, ptr1);
 +    bRest           =
 +        do_numbering(natoms, groups, nvcm, ptr1, grps, gnames, egcVCM,
 +                     restnm, nvcm == 0 ? egrptpALL_GENREST : egrptpPART, bVerbose, wi);
 +    if (bRest)
 +    {
 +        warning(wi, "Some atoms are not part of any center of mass motion removal group.\n"
 +                "This may lead to artifacts.\n"
 +                "In most cases one should use one group for the whole system.");
 +    }
 +
 +    /* Now we have filled the freeze struct, so we can calculate NRDF */
 +    calc_nrdf(mtop, ir, gnames);
 +
 +    if (v && NULL)
 +    {
 +        real fac, ntot = 0;
 +
 +        /* Must check per group! */
 +        for (i = 0; (i < ir->opts.ngtc); i++)
 +        {
 +            ntot += ir->opts.nrdf[i];
 +        }
 +        if (ntot != (DIM*natoms))
 +        {
 +            fac = sqrt(ntot/(DIM*natoms));
 +            if (bVerbose)
 +            {
 +                fprintf(stderr, "Scaling velocities by a factor of %.3f to account for constraints\n"
 +                        "and removal of center of mass motion\n", fac);
 +            }
 +            for (i = 0; (i < natoms); i++)
 +            {
 +                svmul(fac, v[i], v[i]);
 +            }
 +        }
 +    }
 +
 +    nuser = str_nelem(is->user1, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser1,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nuser = str_nelem(is->user2, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser2,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nuser = str_nelem(is->x_compressed_groups, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcCompressedX,
 +                 restnm, egrptpONE, bVerbose, wi);
 +    nofg = str_nelem(is->orirefitgrp, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nofg, ptr1, grps, gnames, egcORFIT,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +
 +    /* QMMM input processing */
 +    nQMg          = str_nelem(is->QMMM, MAXPTR, ptr1);
 +    nQMmethod     = str_nelem(is->QMmethod, MAXPTR, ptr2);
 +    nQMbasis      = str_nelem(is->QMbasis, MAXPTR, ptr3);
 +    if ((nQMmethod != nQMg) || (nQMbasis != nQMg))
 +    {
 +        gmx_fatal(FARGS, "Invalid QMMM input: %d groups %d basissets"
 +                  " and %d methods\n", nQMg, nQMbasis, nQMmethod);
 +    }
 +    /* group rest, if any, is always MM! */
 +    do_numbering(natoms, groups, nQMg, ptr1, grps, gnames, egcQMMM,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr            = nQMg; /*atoms->grps[egcQMMM].nr;*/
 +    ir->opts.ngQM = nQMg;
 +    snew(ir->opts.QMmethod, nr);
 +    snew(ir->opts.QMbasis, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        /* input consists of strings: RHF CASSCF PM3 .. These need to be
 +         * converted to the corresponding enum in names.c
 +         */
 +        ir->opts.QMmethod[i] = search_QMstring(ptr2[i], eQMmethodNR,
 +                                               eQMmethod_names);
 +        ir->opts.QMbasis[i]  = search_QMstring(ptr3[i], eQMbasisNR,
 +                                               eQMbasis_names);
 +
 +    }
 +    nQMmult   = str_nelem(is->QMmult, MAXPTR, ptr1);
 +    nQMcharge = str_nelem(is->QMcharge, MAXPTR, ptr2);
 +    nbSH      = str_nelem(is->bSH, MAXPTR, ptr3);
 +    snew(ir->opts.QMmult, nr);
 +    snew(ir->opts.QMcharge, nr);
 +    snew(ir->opts.bSH, nr);
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.QMmult[i]   = strtol(ptr1[i], NULL, 10);
 +        ir->opts.QMcharge[i] = strtol(ptr2[i], NULL, 10);
 +        ir->opts.bSH[i]      = (gmx_strncasecmp(ptr3[i], "Y", 1) == 0);
 +    }
 +
 +    nCASelec  = str_nelem(is->CASelectrons, MAXPTR, ptr1);
 +    nCASorb   = str_nelem(is->CASorbitals, MAXPTR, ptr2);
 +    snew(ir->opts.CASelectrons, nr);
 +    snew(ir->opts.CASorbitals, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.CASelectrons[i] = strtol(ptr1[i], NULL, 10);
 +        ir->opts.CASorbitals[i]  = strtol(ptr2[i], NULL, 10);
 +    }
 +    /* special optimization options */
 +
 +    nbOPT = str_nelem(is->bOPT, MAXPTR, ptr1);
 +    nbTS  = str_nelem(is->bTS, MAXPTR, ptr2);
 +    snew(ir->opts.bOPT, nr);
 +    snew(ir->opts.bTS, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.bOPT[i] = (gmx_strncasecmp(ptr1[i], "Y", 1) == 0);
 +        ir->opts.bTS[i]  = (gmx_strncasecmp(ptr2[i], "Y", 1) == 0);
 +    }
 +    nSAon     = str_nelem(is->SAon, MAXPTR, ptr1);
 +    nSAoff    = str_nelem(is->SAoff, MAXPTR, ptr2);
 +    nSAsteps  = str_nelem(is->SAsteps, MAXPTR, ptr3);
 +    snew(ir->opts.SAon, nr);
 +    snew(ir->opts.SAoff, nr);
 +    snew(ir->opts.SAsteps, nr);
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.SAon[i]    = strtod(ptr1[i], NULL);
 +        ir->opts.SAoff[i]   = strtod(ptr2[i], NULL);
 +        ir->opts.SAsteps[i] = strtol(ptr3[i], NULL, 10);
 +    }
 +    /* end of QMMM input */
 +
 +    if (bVerbose)
 +    {
 +        for (i = 0; (i < egcNR); i++)
 +        {
 +            fprintf(stderr, "%-16s has %d element(s):", gtypes[i], groups->grps[i].nr);
 +            for (j = 0; (j < groups->grps[i].nr); j++)
 +            {
 +                fprintf(stderr, " %s", *(groups->grpname[groups->grps[i].nm_ind[j]]));
 +            }
 +            fprintf(stderr, "\n");
 +        }
 +    }
 +
 +    nr = groups->grps[egcENER].nr;
 +    snew(ir->opts.egp_flags, nr*nr);
 +
 +    bExcl = do_egp_flag(ir, groups, "energygrp-excl", is->egpexcl, EGP_EXCL);
 +    if (bExcl && ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        warning_error(wi, "Energy group exclusions are not (yet) implemented for the Verlet scheme");
 +    }
 +    if (bExcl && EEL_FULL(ir->coulombtype))
 +    {
 +        warning(wi, "Can not exclude the lattice Coulomb energy between energy groups");
 +    }
 +
 +    bTable = do_egp_flag(ir, groups, "energygrp-table", is->egptable, EGP_TABLE);
 +    if (bTable && !(ir->vdwtype == evdwUSER) &&
 +        !(ir->coulombtype == eelUSER) && !(ir->coulombtype == eelPMEUSER) &&
 +        !(ir->coulombtype == eelPMEUSERSWITCH))
 +    {
 +        gmx_fatal(FARGS, "Can only have energy group pair tables in combination with user tables for VdW and/or Coulomb");
 +    }
 +
 +    decode_cos(is->efield_x, &(ir->ex[XX]));
 +    decode_cos(is->efield_xt, &(ir->et[XX]));
 +    decode_cos(is->efield_y, &(ir->ex[YY]));
 +    decode_cos(is->efield_yt, &(ir->et[YY]));
 +    decode_cos(is->efield_z, &(ir->ex[ZZ]));
 +    decode_cos(is->efield_zt, &(ir->et[ZZ]));
 +
 +    if (ir->bAdress)
 +    {
 +        do_adress_index(ir->adress, groups, gnames, &(ir->opts), wi);
 +    }
 +
 +    for (i = 0; (i < grps->nr); i++)
 +    {
 +        sfree(gnames[i]);
 +    }
 +    sfree(gnames);
 +    done_blocka(grps);
 +    sfree(grps);
 +
 +}
 +
 +
 +
 +static void check_disre(gmx_mtop_t *mtop)
 +{
 +    gmx_ffparams_t *ffparams;
 +    t_functype     *functype;
 +    t_iparams      *ip;
 +    int             i, ndouble, ftype;
 +    int             label, old_label;
 +
 +    if (gmx_mtop_ftype_count(mtop, F_DISRES) > 0)
 +    {
 +        ffparams  = &mtop->ffparams;
 +        functype  = ffparams->functype;
 +        ip        = ffparams->iparams;
 +        ndouble   = 0;
 +        old_label = -1;
 +        for (i = 0; i < ffparams->ntypes; i++)
 +        {
 +            ftype = functype[i];
 +            if (ftype == F_DISRES)
 +            {
 +                label = ip[i].disres.label;
 +                if (label == old_label)
 +                {
 +                    fprintf(stderr, "Distance restraint index %d occurs twice\n", label);
 +                    ndouble++;
 +                }
 +                old_label = label;
 +            }
 +        }
 +        if (ndouble > 0)
 +        {
 +            gmx_fatal(FARGS, "Found %d double distance restraint indices,\n"
 +                      "probably the parameters for multiple pairs in one restraint "
 +                      "are not identical\n", ndouble);
 +        }
 +    }
 +}
 +
 +static gmx_bool absolute_reference(t_inputrec *ir, gmx_mtop_t *sys,
 +                                   gmx_bool posres_only,
 +                                   ivec AbsRef)
 +{
 +    int                  d, g, i;
 +    gmx_mtop_ilistloop_t iloop;
 +    t_ilist             *ilist;
 +    int                  nmol;
 +    t_iparams           *pr;
 +
 +    clear_ivec(AbsRef);
 +
 +    if (!posres_only)
 +    {
 +        /* Check the COM */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            AbsRef[d] = (d < ndof_com(ir) ? 0 : 1);
 +        }
 +        /* Check for freeze groups */
 +        for (g = 0; g < ir->opts.ngfrz; g++)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (ir->opts.nFreeze[g][d] != 0)
 +                {
 +                    AbsRef[d] = 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Check for position restraints */
 +    iloop = gmx_mtop_ilistloop_init(sys);
 +    while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol))
 +    {
 +        if (nmol > 0 &&
 +            (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0))
 +        {
 +            for (i = 0; i < ilist[F_POSRES].nr; i += 2)
 +            {
 +                pr = &sys->ffparams.iparams[ilist[F_POSRES].iatoms[i]];
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    if (pr->posres.fcA[d] != 0)
 +                    {
 +                        AbsRef[d] = 1;
 +                    }
 +                }
 +            }
 +            for (i = 0; i < ilist[F_FBPOSRES].nr; i += 2)
 +            {
 +                /* Check for flat-bottom posres */
 +                pr = &sys->ffparams.iparams[ilist[F_FBPOSRES].iatoms[i]];
 +                if (pr->fbposres.k != 0)
 +                {
 +                    switch (pr->fbposres.geom)
 +                    {
 +                        case efbposresSPHERE:
 +                            AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1;
 +                            break;
 +                        case efbposresCYLINDER:
 +                            AbsRef[XX] = AbsRef[YY] = 1;
 +                            break;
 +                        case efbposresX: /* d=XX */
 +                        case efbposresY: /* d=YY */
 +                        case efbposresZ: /* d=ZZ */
 +                            d         = pr->fbposres.geom - efbposresX;
 +                            AbsRef[d] = 1;
 +                            break;
 +                        default:
 +                            gmx_fatal(FARGS, " Invalid geometry for flat-bottom position restraint.\n"
 +                                      "Expected nr between 1 and %d. Found %d\n", efbposresNR-1,
 +                                      pr->fbposres.geom);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0);
 +}
 +
 +static void
 +check_combination_rule_differences(const gmx_mtop_t *mtop, int state,
 +                                   gmx_bool *bC6ParametersWorkWithGeometricRules,
 +                                   gmx_bool *bC6ParametersWorkWithLBRules,
 +                                   gmx_bool *bLBRulesPossible)
 +{
 +    int           ntypes, tpi, tpj, thisLBdiff, thisgeomdiff;
 +    int          *typecount;
 +    real          tol;
 +    double        geometricdiff, LBdiff;
 +    double        c6i, c6j, c12i, c12j;
 +    double        c6, c6_geometric, c6_LB;
 +    double        sigmai, sigmaj, epsi, epsj;
 +    gmx_bool      bCanDoLBRules, bCanDoGeometricRules;
 +    const char   *ptr;
 +
 +    /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed)
 +     * force-field floating point parameters.
 +     */
 +    tol = 1e-5;
 +    ptr = getenv("GMX_LJCOMB_TOL");
 +    if (ptr != NULL)
 +    {
 +        double dbl;
 +
 +        sscanf(ptr, "%lf", &dbl);
 +        tol = dbl;
 +    }
 +
 +    *bC6ParametersWorkWithLBRules         = TRUE;
 +    *bC6ParametersWorkWithGeometricRules  = TRUE;
 +    bCanDoLBRules                         = TRUE;
 +    bCanDoGeometricRules                  = TRUE;
 +    ntypes                                = mtop->ffparams.atnr;
 +    snew(typecount, ntypes);
 +    gmx_mtop_count_atomtypes(mtop, state, typecount);
 +    geometricdiff           = LBdiff = 0.0;
 +    *bLBRulesPossible       = TRUE;
 +    for (tpi = 0; tpi < ntypes; ++tpi)
 +    {
 +        c6i  = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c6;
 +        c12i = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c12;
 +        for (tpj = tpi; tpj < ntypes; ++tpj)
 +        {
 +            c6j          = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c6;
 +            c12j         = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c12;
 +            c6           = mtop->ffparams.iparams[ntypes * tpi + tpj].lj.c6;
 +            c6_geometric = sqrt(c6i * c6j);
 +            if (!gmx_numzero(c6_geometric))
 +            {
 +                if (!gmx_numzero(c12i) && !gmx_numzero(c12j))
 +                {
 +                    sigmai   = pow(c12i / c6i, 1.0/6.0);
 +                    sigmaj   = pow(c12j / c6j, 1.0/6.0);
 +                    epsi     = c6i * c6i /(4.0 * c12i);
 +                    epsj     = c6j * c6j /(4.0 * c12j);
 +                    c6_LB    = 4.0 * pow(epsi * epsj, 1.0/2.0) * pow(0.5 * (sigmai + sigmaj), 6);
 +                }
 +                else
 +                {
 +                    *bLBRulesPossible = FALSE;
 +                    c6_LB             = c6_geometric;
 +                }
 +                bCanDoLBRules = gmx_within_tol(c6_LB, c6, tol);
 +            }
 +
 +            if (FALSE == bCanDoLBRules)
 +            {
 +                *bC6ParametersWorkWithLBRules = FALSE;
 +            }
 +
 +            bCanDoGeometricRules = gmx_within_tol(c6_geometric, c6, tol);
 +
 +            if (FALSE == bCanDoGeometricRules)
 +            {
 +                *bC6ParametersWorkWithGeometricRules = FALSE;
 +            }
 +        }
 +    }
 +    sfree(typecount);
 +}
 +
 +static void
 +check_combination_rules(const t_inputrec *ir, const gmx_mtop_t *mtop,
 +                        warninp_t wi)
 +{
 +    char     err_buf[256];
 +    gmx_bool bLBRulesPossible, bC6ParametersWorkWithGeometricRules, bC6ParametersWorkWithLBRules;
 +
 +    check_combination_rule_differences(mtop, 0,
 +                                       &bC6ParametersWorkWithGeometricRules,
 +                                       &bC6ParametersWorkWithLBRules,
 +                                       &bLBRulesPossible);
 +    if (ir->ljpme_combination_rule == eljpmeLB)
 +    {
 +        if (FALSE == bC6ParametersWorkWithLBRules || FALSE == bLBRulesPossible)
 +        {
 +            warning(wi, "You are using arithmetic-geometric combination rules "
 +                    "in LJ-PME, but your non-bonded C6 parameters do not "
 +                    "follow these rules.");
 +        }
 +    }
 +    else
 +    {
 +        if (FALSE == bC6ParametersWorkWithGeometricRules)
 +        {
 +            if (ir->eDispCorr != edispcNO)
 +            {
 +                warning_note(wi, "You are using geometric combination rules in "
 +                             "LJ-PME, but your non-bonded C6 parameters do "
 +                             "not follow these rules. "
 +                             "This will introduce very small errors in the forces and energies in "
 +                             "your simulations. Dispersion correction will correct total energy "
 +                             "and/or pressure for isotropic systems, but not forces or surface tensions.");
 +            }
 +            else
 +            {
 +                warning_note(wi, "You are using geometric combination rules in "
 +                             "LJ-PME, but your non-bonded C6 parameters do "
 +                             "not follow these rules. "
 +                             "This will introduce very small errors in the forces and energies in "
 +                             "your simulations. If your system is homogeneous, consider using dispersion correction "
 +                             "for the total energy and pressure.");
 +            }
 +        }
 +    }
 +}
 +
 +void triple_check(const char *mdparin, t_inputrec *ir, gmx_mtop_t *sys,
 +                  warninp_t wi)
 +{
 +    char                      err_buf[STRLEN];
 +    int                       i, m, c, nmol, npct;
 +    gmx_bool                  bCharge, bAcc;
 +    real                      gdt_max, *mgrp, mt;
 +    rvec                      acc;
 +    gmx_mtop_atomloop_block_t aloopb;
 +    gmx_mtop_atomloop_all_t   aloop;
 +    t_atom                   *atom;
 +    ivec                      AbsRef;
 +    char                      warn_buf[STRLEN];
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    if (ir->cutoff_scheme == ecutsVERLET &&
 +        ir->verletbuf_tol > 0 &&
 +        ir->nstlist > 1 &&
 +        ((EI_MD(ir->eI) || EI_SD(ir->eI)) &&
 +         (ir->etc == etcVRESCALE || ir->etc == etcBERENDSEN)))
 +    {
 +        /* Check if a too small Verlet buffer might potentially
 +         * cause more drift than the thermostat can couple off.
 +         */
 +        /* Temperature error fraction for warning and suggestion */
 +        const real T_error_warn    = 0.002;
 +        const real T_error_suggest = 0.001;
 +        /* For safety: 2 DOF per atom (typical with constraints) */
 +        const real nrdf_at         = 2;
 +        real       T, tau, max_T_error;
 +        int        i;
 +
 +        T   = 0;
 +        tau = 0;
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            T   = max(T, ir->opts.ref_t[i]);
 +            tau = max(tau, ir->opts.tau_t[i]);
 +        }
 +        if (T > 0)
 +        {
 +            /* This is a worst case estimate of the temperature error,
 +             * assuming perfect buffer estimation and no cancelation
 +             * of errors. The factor 0.5 is because energy distributes
 +             * equally over Ekin and Epot.
 +             */
 +            max_T_error = 0.5*tau*ir->verletbuf_tol/(nrdf_at*BOLTZ*T);
 +            if (max_T_error > T_error_warn)
 +            {
 +                sprintf(warn_buf, "With a verlet-buffer-tolerance of %g kJ/mol/ps, a reference temperature of %g and a tau_t of %g, your temperature might be off by up to %.1f%%. To ensure the error is below %.1f%%, decrease verlet-buffer-tolerance to %.0e or decrease tau_t.",
 +                        ir->verletbuf_tol, T, tau,
 +                        100*max_T_error,
 +                        100*T_error_suggest,
 +                        ir->verletbuf_tol*T_error_suggest/max_T_error);
 +                warning(wi, warn_buf);
 +            }
 +        }
 +    }
 +
 +    if (ETC_ANDERSEN(ir->etc))
 +    {
 +        int i;
 +
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            sprintf(err_buf, "all tau_t must currently be equal using Andersen temperature control, violated for group %d", i);
 +            CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]);
 +            sprintf(err_buf, "all tau_t must be postive using Andersen temperature control, tau_t[%d]=%10.6f",
 +                    i, ir->opts.tau_t[i]);
 +            CHECK(ir->opts.tau_t[i] < 0);
 +        }
 +
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            int nsteps = (int)(ir->opts.tau_t[i]/ir->delta_t);
 +            sprintf(err_buf, "tau_t/delta_t for group %d for temperature control method %s must be a multiple of nstcomm (%d), as velocities of atoms in coupled groups are randomized every time step. The input tau_t (%8.3f) leads to %d steps per randomization", i, etcoupl_names[ir->etc], ir->nstcomm, ir->opts.tau_t[i], nsteps);
 +            CHECK((nsteps % ir->nstcomm) && (ir->etc == etcANDERSENMASSIVE));
 +        }
 +    }
 +
 +    if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != eiBD &&
 +        ir->comm_mode == ecmNO &&
 +        !(absolute_reference(ir, sys, FALSE, AbsRef) || ir->nsteps <= 10) &&
 +        !ETC_ANDERSEN(ir->etc))
 +    {
 +        warning(wi, "You are not using center of mass motion removal (mdp option comm-mode), numerical rounding errors can lead to build up of kinetic energy of the center of mass");
 +    }
 +
 +    /* Check for pressure coupling with absolute position restraints */
 +    if (ir->epc != epcNO && ir->refcoord_scaling == erscNO)
 +    {
 +        absolute_reference(ir, sys, TRUE, AbsRef);
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                if (AbsRef[m] && norm2(ir->compress[m]) > 0)
 +                {
 +                    warning(wi, "You are using pressure coupling with absolute position restraints, this will give artifacts. Use the refcoord_scaling option.");
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    bCharge = FALSE;
 +    aloopb  = gmx_mtop_atomloop_block_init(sys);
 +    while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol))
 +    {
 +        if (atom->q != 0 || atom->qB != 0)
 +        {
 +            bCharge = TRUE;
 +        }
 +    }
 +
 +    if (!bCharge)
 +    {
 +        if (EEL_FULL(ir->coulombtype))
 +        {
 +            sprintf(err_buf,
 +                    "You are using full electrostatics treatment %s for a system without charges.\n"
 +                    "This costs a lot of performance for just processing zeros, consider using %s instead.\n",
 +                    EELTYPE(ir->coulombtype), EELTYPE(eelCUT));
 +            warning(wi, err_buf);
 +        }
 +    }
 +    else
 +    {
 +        if (ir->coulombtype == eelCUT && ir->rcoulomb > 0 && !ir->implicit_solvent)
 +        {
 +            sprintf(err_buf,
 +                    "You are using a plain Coulomb cut-off, which might produce artifacts.\n"
 +                    "You might want to consider using %s electrostatics.\n",
 +                    EELTYPE(eelPME));
 +            warning_note(wi, err_buf);
 +        }
 +    }
 +
 +    /* Check if combination rules used in LJ-PME are the same as in the force field */
 +    if (EVDW_PME(ir->vdwtype))
 +    {
 +        check_combination_rules(ir, sys, wi);
 +    }
 +
 +    /* Generalized reaction field */
 +    if (ir->opts.ngtc == 0)
 +    {
 +        sprintf(err_buf, "No temperature coupling while using coulombtype %s",
 +                eel_names[eelGRF]);
 +        CHECK(ir->coulombtype == eelGRF);
 +    }
 +    else
 +    {
 +        sprintf(err_buf, "When using coulombtype = %s"
 +                " ref-t for temperature coupling should be > 0",
 +                eel_names[eelGRF]);
 +        CHECK((ir->coulombtype == eelGRF) && (ir->opts.ref_t[0] <= 0));
 +    }
 +
 +    if (ir->eI == eiSD1 &&
 +        (gmx_mtop_ftype_count(sys, F_CONSTR) > 0 ||
 +         gmx_mtop_ftype_count(sys, F_SETTLE) > 0))
 +    {
 +        sprintf(warn_buf, "With constraints integrator %s is less accurate, consider using %s instead", ei_names[ir->eI], ei_names[eiSD2]);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    bAcc = FALSE;
 +    for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +    {
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fabs(ir->opts.acc[i][m]) > 1e-6)
 +            {
 +                bAcc = TRUE;
 +            }
 +        }
 +    }
 +    if (bAcc)
 +    {
 +        clear_rvec(acc);
 +        snew(mgrp, sys->groups.grps[egcACC].nr);
 +        aloop = gmx_mtop_atomloop_all_init(sys);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            mgrp[ggrpnr(&sys->groups, egcACC, i)] += atom->m;
 +        }
 +        mt = 0.0;
 +        for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                acc[m] += ir->opts.acc[i][m]*mgrp[i];
 +            }
 +            mt += mgrp[i];
 +        }
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fabs(acc[m]) > 1e-6)
 +            {
 +                const char *dim[DIM] = { "X", "Y", "Z" };
 +                fprintf(stderr,
 +                        "Net Acceleration in %s direction, will %s be corrected\n",
 +                        dim[m], ir->nstcomm != 0 ? "" : "not");
 +                if (ir->nstcomm != 0 && m < ndof_com(ir))
 +                {
 +                    acc[m] /= mt;
 +                    for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +                    {
 +                        ir->opts.acc[i][m] -= acc[m];
 +                    }
 +                }
 +            }
 +        }
 +        sfree(mgrp);
 +    }
 +
 +    if (ir->efep != efepNO && ir->fepvals->sc_alpha != 0 &&
 +        !gmx_within_tol(sys->ffparams.reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +    {
 +        gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12");
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        gmx_bool bPullAbsoluteRef;
 +
 +        bPullAbsoluteRef = FALSE;
 +        for (i = 0; i < ir->pull->ncoord; i++)
 +        {
 +            bPullAbsoluteRef = bPullAbsoluteRef ||
 +                ir->pull->coord[i].group[0] == 0 ||
 +                ir->pull->coord[i].group[1] == 0;
 +        }
 +        if (bPullAbsoluteRef)
 +        {
 +            absolute_reference(ir, sys, FALSE, AbsRef);
 +            for (m = 0; m < DIM; m++)
 +            {
 +                if (ir->pull->dim[m] && !AbsRef[m])
 +                {
 +                    warning(wi, "You are using an absolute reference for pulling, but the rest of the system does not have an absolute reference. This will lead to artifacts.");
 +                    break;
 +                }
 +            }
 +        }
 +
 +        if (ir->pull->eGeom == epullgDIRPBC)
 +        {
 +            for (i = 0; i < 3; i++)
 +            {
 +                for (m = 0; m <= i; m++)
 +                {
 +                    if ((ir->epc != epcNO && ir->compress[i][m] != 0) ||
 +                        ir->deform[i][m] != 0)
 +                    {
 +                        for (c = 0; c < ir->pull->ncoord; c++)
 +                        {
 +                            if (ir->pull->coord[c].vec[m] != 0)
 +                            {
 +                                gmx_fatal(FARGS, "Can not have dynamic box while using pull geometry '%s' (dim %c)", EPULLGEOM(ir->pull->eGeom), 'x'+m);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    check_disre(sys);
 +}
 +
 +void double_check(t_inputrec *ir, matrix box, gmx_bool bConstr, warninp_t wi)
 +{
 +    real        min_size;
 +    gmx_bool    bTWIN;
 +    char        warn_buf[STRLEN];
 +    const char *ptr;
 +
 +    ptr = check_box(ir->ePBC, box);
 +    if (ptr)
 +    {
 +        warning_error(wi, ptr);
 +    }
 +
 +    if (bConstr && ir->eConstrAlg == econtSHAKE)
 +    {
 +        if (ir->shake_tol <= 0.0)
 +        {
 +            sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n",
 +                    ir->shake_tol);
 +            warning_error(wi, warn_buf);
 +        }
 +
 +        if (IR_TWINRANGE(*ir) && ir->nstlist > 1)
 +        {
 +            sprintf(warn_buf, "With twin-range cut-off's and SHAKE the virial and the pressure are incorrect.");
 +            if (ir->epc == epcNO)
 +            {
 +                warning(wi, warn_buf);
 +            }
 +            else
 +            {
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +    }
 +
 +    if ( (ir->eConstrAlg == econtLINCS) && bConstr)
 +    {
 +        /* If we have Lincs constraints: */
 +        if (ir->eI == eiMD && ir->etc == etcNO &&
 +            ir->eConstrAlg == econtLINCS && ir->nLincsIter == 1)
 +        {
 +            sprintf(warn_buf, "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n");
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        if ((ir->eI == eiCG || ir->eI == eiLBFGS) && (ir->nProjOrder < 8))
 +        {
 +            sprintf(warn_buf, "For accurate %s with LINCS constraints, lincs-order should be 8 or more.", ei_names[ir->eI]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir->epc == epcMTTK)
 +        {
 +            warning_error(wi, "MTTK not compatible with lincs -- use shake instead.");
 +        }
 +    }
 +
 +    if (bConstr && ir->epc == epcMTTK)
 +    {
 +        warning_note(wi, "MTTK with constraints is deprecated, and will be removed in GROMACS 5.1");
 +    }
 +
 +    if (ir->LincsWarnAngle > 90.0)
 +    {
 +        sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n");
 +        warning(wi, warn_buf);
 +        ir->LincsWarnAngle = 90.0;
 +    }
 +
 +    if (ir->ePBC != epbcNONE)
 +    {
 +        if (ir->nstlist == 0)
 +        {
 +            warning(wi, "With nstlist=0 atoms are only put into the box at step 0, therefore drifting atoms might cause the simulation to crash.");
 +        }
 +        bTWIN = (ir->rlistlong > ir->rlist);
 +        if (ir->ns_type == ensGRID)
 +        {
 +            if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box))
 +            {
 +                sprintf(warn_buf, "ERROR: The cut-off length is longer than half the shortest box vector or longer than the smallest box diagonal element. Increase the box size or decrease %s.\n",
 +                        bTWIN ? (ir->rcoulomb == ir->rlistlong ? "rcoulomb" : "rvdw") : "rlist");
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +        else
 +        {
 +            min_size = min(box[XX][XX], min(box[YY][YY], box[ZZ][ZZ]));
 +            if (2*ir->rlistlong >= min_size)
 +            {
 +                sprintf(warn_buf, "ERROR: One of the box lengths is smaller than twice the cut-off length. Increase the box size or decrease rlist.");
 +                warning_error(wi, warn_buf);
 +                if (TRICLINIC(box))
 +                {
 +                    fprintf(stderr, "Grid search might allow larger cut-off's than simple search with triclinic boxes.");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void check_chargegroup_radii(const gmx_mtop_t *mtop, const t_inputrec *ir,
 +                             rvec *x,
 +                             warninp_t wi)
 +{
 +    real rvdw1, rvdw2, rcoul1, rcoul2;
 +    char warn_buf[STRLEN];
 +
 +    calc_chargegroup_radii(mtop, x, &rvdw1, &rvdw2, &rcoul1, &rcoul2);
 +
 +    if (rvdw1 > 0)
 +    {
 +        printf("Largest charge group radii for Van der Waals: %5.3f, %5.3f nm\n",
 +               rvdw1, rvdw2);
 +    }
 +    if (rcoul1 > 0)
 +    {
 +        printf("Largest charge group radii for Coulomb:       %5.3f, %5.3f nm\n",
 +               rcoul1, rcoul2);
 +    }
 +
 +    if (ir->rlist > 0)
 +    {
 +        if (rvdw1  + rvdw2  > ir->rlist ||
 +            rcoul1 + rcoul2 > ir->rlist)
 +        {
 +            sprintf(warn_buf,
 +                    "The sum of the two largest charge group radii (%f) "
 +                    "is larger than rlist (%f)\n",
 +                    max(rvdw1+rvdw2, rcoul1+rcoul2), ir->rlist);
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            /* Here we do not use the zero at cut-off macro,
 +             * since user defined interactions might purposely
 +             * not be zero at the cut-off.
 +             */
 +            if (ir_vdw_is_zero_at_cutoff(ir) &&
 +                rvdw1 + rvdw2 > ir->rlistlong - ir->rvdw)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group "
 +                        "radii (%f) is larger than %s (%f) - rvdw (%f).\n"
 +                        "With exact cut-offs, better performance can be "
 +                        "obtained with cutoff-scheme = %s, because it "
 +                        "does not use charge groups at all.",
 +                        rvdw1+rvdw2,
 +                        ir->rlistlong > ir->rlist ? "rlistlong" : "rlist",
 +                        ir->rlistlong, ir->rvdw,
 +                        ecutscheme_names[ecutsVERLET]);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +            if (ir_coulomb_is_zero_at_cutoff(ir) &&
 +                rcoul1 + rcoul2 > ir->rlistlong - ir->rcoulomb)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than %s (%f) - rcoulomb (%f).\n"
 +                        "With exact cut-offs, better performance can be obtained with cutoff-scheme = %s, because it does not use charge groups at all.",
 +                        rcoul1+rcoul2,
 +                        ir->rlistlong > ir->rlist ? "rlistlong" : "rlist",
 +                        ir->rlistlong, ir->rcoulomb,
 +                        ecutscheme_names[ecutsVERLET]);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +}
index 67cd36f34184ba820b4ab4a6196dd56fc135078a,0000000000000000000000000000000000000000..8db62ab92d8cc649abe64b7c8ae31f4cefd44611
mode 100644,000000..100644
--- /dev/null
@@@ -1,3283 -1,0 +1,3288 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include <assert.h>
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "vec.h"
 +#include "gromacs/math/utilities.h"
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "physics.h"
 +#include "force.h"
 +#include "tables.h"
 +#include "nonbonded.h"
 +#include "invblock.h"
 +#include "names.h"
 +#include "network.h"
 +#include "pbc.h"
 +#include "ns.h"
 +#include "mshift.h"
 +#include "txtdump.h"
 +#include "coulomb.h"
 +#include "md_support.h"
 +#include "md_logging.h"
 +#include "domdec.h"
 +#include "qmmm.h"
 +#include "copyrite.h"
 +#include "mtop_util.h"
 +#include "nbnxn_simd.h"
 +#include "nbnxn_search.h"
 +#include "nbnxn_atomdata.h"
 +#include "nbnxn_consts.h"
 +#include "gmx_omp_nthreads.h"
 +#include "gmx_detect_hardware.h"
 +#include "inputrec.h"
 +
 +#include "types/nbnxn_cuda_types_ext.h"
 +#include "gpu_utils.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +#include "pmalloc_cuda.h"
 +
 +t_forcerec *mk_forcerec(void)
 +{
 +    t_forcerec *fr;
 +
 +    snew(fr, 1);
 +
 +    return fr;
 +}
 +
 +#ifdef DEBUG
 +static void pr_nbfp(FILE *fp, real *nbfp, gmx_bool bBHAM, int atnr)
 +{
 +    int i, j;
 +
 +    for (i = 0; (i < atnr); i++)
 +    {
 +        for (j = 0; (j < atnr); j++)
 +        {
 +            fprintf(fp, "%2d - %2d", i, j);
 +            if (bBHAM)
 +            {
 +                fprintf(fp, "  a=%10g, b=%10g, c=%10g\n", BHAMA(nbfp, atnr, i, j),
 +                        BHAMB(nbfp, atnr, i, j), BHAMC(nbfp, atnr, i, j)/6.0);
 +            }
 +            else
 +            {
 +                fprintf(fp, "  c6=%10g, c12=%10g\n", C6(nbfp, atnr, i, j)/6.0,
 +                        C12(nbfp, atnr, i, j)/12.0);
 +            }
 +        }
 +    }
 +}
 +#endif
 +
 +static real *mk_nbfp(const gmx_ffparams_t *idef, gmx_bool bBHAM)
 +{
 +    real *nbfp;
 +    int   i, j, k, atnr;
 +
 +    atnr = idef->atnr;
 +    if (bBHAM)
 +    {
 +        snew(nbfp, 3*atnr*atnr);
 +        for (i = k = 0; (i < atnr); i++)
 +        {
 +            for (j = 0; (j < atnr); j++, k++)
 +            {
 +                BHAMA(nbfp, atnr, i, j) = idef->iparams[k].bham.a;
 +                BHAMB(nbfp, atnr, i, j) = idef->iparams[k].bham.b;
 +                /* nbfp now includes the 6.0 derivative prefactor */
 +                BHAMC(nbfp, atnr, i, j) = idef->iparams[k].bham.c*6.0;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        snew(nbfp, 2*atnr*atnr);
 +        for (i = k = 0; (i < atnr); i++)
 +        {
 +            for (j = 0; (j < atnr); j++, k++)
 +            {
 +                /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                C6(nbfp, atnr, i, j)   = idef->iparams[k].lj.c6*6.0;
 +                C12(nbfp, atnr, i, j)  = idef->iparams[k].lj.c12*12.0;
 +            }
 +        }
 +    }
 +
 +    return nbfp;
 +}
 +
 +static real *make_ljpme_c6grid(const gmx_ffparams_t *idef, t_forcerec *fr)
 +{
 +    int   i, j, k, atnr;
 +    real  c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 +    real *grid;
 +
 +    /* For LJ-PME simulations, we correct the energies with the reciprocal space
 +     * inside of the cut-off. To do this the non-bonded kernels needs to have
 +     * access to the C6-values used on the reciprocal grid in pme.c
 +     */
 +
 +    atnr = idef->atnr;
 +    snew(grid, 2*atnr*atnr);
 +    for (i = k = 0; (i < atnr); i++)
 +    {
 +        for (j = 0; (j < atnr); j++, k++)
 +        {
 +            c6i  = idef->iparams[i*(atnr+1)].lj.c6;
 +            c12i = idef->iparams[i*(atnr+1)].lj.c12;
 +            c6j  = idef->iparams[j*(atnr+1)].lj.c6;
 +            c12j = idef->iparams[j*(atnr+1)].lj.c12;
 +            c6   = sqrt(c6i * c6j);
 +            if (fr->ljpme_combination_rule == eljpmeLB
 +                && !gmx_numzero(c6) && !gmx_numzero(c12i) && !gmx_numzero(c12j))
 +            {
 +                sigmai = pow(c12i / c6i, 1.0/6.0);
 +                sigmaj = pow(c12j / c6j, 1.0/6.0);
 +                epsi   = c6i * c6i / c12i;
 +                epsj   = c6j * c6j / c12j;
 +                c6     = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
 +            }
 +            /* Store the elements at the same relative positions as C6 in nbfp in order
 +             * to simplify access in the kernels
 +             */
 +            grid[2*(atnr*i+j)] = c6*6.0;
 +        }
 +    }
 +    return grid;
 +}
 +
 +static real *mk_nbfp_combination_rule(const gmx_ffparams_t *idef, int comb_rule)
 +{
 +    real *nbfp;
 +    int   i, j, k, atnr;
 +    real  c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 +    real  c6, c12;
 +
 +    atnr = idef->atnr;
 +    snew(nbfp, 2*atnr*atnr);
 +    for (i = 0; i < atnr; ++i)
 +    {
 +        for (j = 0; j < atnr; ++j)
 +        {
 +            c6i  = idef->iparams[i*(atnr+1)].lj.c6;
 +            c12i = idef->iparams[i*(atnr+1)].lj.c12;
 +            c6j  = idef->iparams[j*(atnr+1)].lj.c6;
 +            c12j = idef->iparams[j*(atnr+1)].lj.c12;
 +            c6   = sqrt(c6i  * c6j);
 +            c12  = sqrt(c12i * c12j);
 +            if (comb_rule == eCOMB_ARITHMETIC
 +                && !gmx_numzero(c6) && !gmx_numzero(c12))
 +            {
 +                sigmai = pow(c12i / c6i, 1.0/6.0);
 +                sigmaj = pow(c12j / c6j, 1.0/6.0);
 +                epsi   = c6i * c6i / c12i;
 +                epsj   = c6j * c6j / c12j;
 +                c6     = epsi * epsj * pow(0.5*(sigmai+sigmaj), 6);
 +                c12    = epsi * epsj * pow(0.5*(sigmai+sigmaj), 12);
 +            }
 +            C6(nbfp, atnr, i, j)   = c6*6.0;
 +            C12(nbfp, atnr, i, j)  = c12*12.0;
 +        }
 +    }
 +    return nbfp;
 +}
 +
 +/* This routine sets fr->solvent_opt to the most common solvent in the
 + * system, e.g. esolSPC or esolTIP4P. It will also mark each charge group in
 + * the fr->solvent_type array with the correct type (or esolNO).
 + *
 + * Charge groups that fulfill the conditions but are not identical to the
 + * most common one will be marked as esolNO in the solvent_type array.
 + *
 + * TIP3p is identical to SPC for these purposes, so we call it
 + * SPC in the arrays (Apologies to Bill Jorgensen ;-)
 + *
 + * NOTE: QM particle should not
 + * become an optimized solvent. Not even if there is only one charge
 + * group in the Qm
 + */
 +
 +typedef struct
 +{
 +    int    model;
 +    int    count;
 +    int    vdwtype[4];
 +    real   charge[4];
 +} solvent_parameters_t;
 +
 +static void
 +check_solvent_cg(const gmx_moltype_t    *molt,
 +                 int                     cg0,
 +                 int                     nmol,
 +                 const unsigned char    *qm_grpnr,
 +                 const t_grps           *qm_grps,
 +                 t_forcerec   *          fr,
 +                 int                    *n_solvent_parameters,
 +                 solvent_parameters_t  **solvent_parameters_p,
 +                 int                     cginfo,
 +                 int                    *cg_sp)
 +{
 +    const t_blocka       *excl;
 +    t_atom               *atom;
 +    int                   j, k;
 +    int                   j0, j1, nj;
 +    gmx_bool              perturbed;
 +    gmx_bool              has_vdw[4];
 +    gmx_bool              match;
 +    real                  tmp_charge[4]  = { 0.0 }; /* init to zero to make gcc4.8 happy */
 +    int                   tmp_vdwtype[4] = { 0 };   /* init to zero to make gcc4.8 happy */
 +    int                   tjA;
 +    gmx_bool              qm;
 +    solvent_parameters_t *solvent_parameters;
 +
 +    /* We use a list with parameters for each solvent type.
 +     * Every time we discover a new molecule that fulfills the basic
 +     * conditions for a solvent we compare with the previous entries
 +     * in these lists. If the parameters are the same we just increment
 +     * the counter for that type, and otherwise we create a new type
 +     * based on the current molecule.
 +     *
 +     * Once we've finished going through all molecules we check which
 +     * solvent is most common, and mark all those molecules while we
 +     * clear the flag on all others.
 +     */
 +
 +    solvent_parameters = *solvent_parameters_p;
 +
 +    /* Mark the cg first as non optimized */
 +    *cg_sp = -1;
 +
 +    /* Check if this cg has no exclusions with atoms in other charge groups
 +     * and all atoms inside the charge group excluded.
 +     * We only have 3 or 4 atom solvent loops.
 +     */
 +    if (GET_CGINFO_EXCL_INTER(cginfo) ||
 +        !GET_CGINFO_EXCL_INTRA(cginfo))
 +    {
 +        return;
 +    }
 +
 +    /* Get the indices of the first atom in this charge group */
 +    j0     = molt->cgs.index[cg0];
 +    j1     = molt->cgs.index[cg0+1];
 +
 +    /* Number of atoms in our molecule */
 +    nj     = j1 - j0;
 +
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Moltype '%s': there are %d atoms in this charge group\n",
 +                *molt->name, nj);
 +    }
 +
 +    /* Check if it could be an SPC (3 atoms) or TIP4p (4) water,
 +     * otherwise skip it.
 +     */
 +    if (nj < 3 || nj > 4)
 +    {
 +        return;
 +    }
 +
 +    /* Check if we are doing QM on this group */
 +    qm = FALSE;
 +    if (qm_grpnr != NULL)
 +    {
 +        for (j = j0; j < j1 && !qm; j++)
 +        {
 +            qm = (qm_grpnr[j] < qm_grps->nr - 1);
 +        }
 +    }
 +    /* Cannot use solvent optimization with QM */
 +    if (qm)
 +    {
 +        return;
 +    }
 +
 +    atom = molt->atoms.atom;
 +
 +    /* Still looks like a solvent, time to check parameters */
 +
 +    /* If it is perturbed (free energy) we can't use the solvent loops,
 +     * so then we just skip to the next molecule.
 +     */
 +    perturbed = FALSE;
 +
 +    for (j = j0; j < j1 && !perturbed; j++)
 +    {
 +        perturbed = PERTURBED(atom[j]);
 +    }
 +
 +    if (perturbed)
 +    {
 +        return;
 +    }
 +
 +    /* Now it's only a question if the VdW and charge parameters
 +     * are OK. Before doing the check we compare and see if they are
 +     * identical to a possible previous solvent type.
 +     * First we assign the current types and charges.
 +     */
 +    for (j = 0; j < nj; j++)
 +    {
 +        tmp_vdwtype[j] = atom[j0+j].type;
 +        tmp_charge[j]  = atom[j0+j].q;
 +    }
 +
 +    /* Does it match any previous solvent type? */
 +    for (k = 0; k < *n_solvent_parameters; k++)
 +    {
 +        match = TRUE;
 +
 +
 +        /* We can only match SPC with 3 atoms and TIP4p with 4 atoms */
 +        if ( (solvent_parameters[k].model == esolSPC   && nj != 3)  ||
 +             (solvent_parameters[k].model == esolTIP4P && nj != 4) )
 +        {
 +            match = FALSE;
 +        }
 +
 +        /* Check that types & charges match for all atoms in molecule */
 +        for (j = 0; j < nj && match == TRUE; j++)
 +        {
 +            if (tmp_vdwtype[j] != solvent_parameters[k].vdwtype[j])
 +            {
 +                match = FALSE;
 +            }
 +            if (tmp_charge[j] != solvent_parameters[k].charge[j])
 +            {
 +                match = FALSE;
 +            }
 +        }
 +        if (match == TRUE)
 +        {
 +            /* Congratulations! We have a matched solvent.
 +             * Flag it with this type for later processing.
 +             */
 +            *cg_sp = k;
 +            solvent_parameters[k].count += nmol;
 +
 +            /* We are done with this charge group */
 +            return;
 +        }
 +    }
 +
 +    /* If we get here, we have a tentative new solvent type.
 +     * Before we add it we must check that it fulfills the requirements
 +     * of the solvent optimized loops. First determine which atoms have
 +     * VdW interactions.
 +     */
 +    for (j = 0; j < nj; j++)
 +    {
 +        has_vdw[j] = FALSE;
 +        tjA        = tmp_vdwtype[j];
 +
 +        /* Go through all other tpes and see if any have non-zero
 +         * VdW parameters when combined with this one.
 +         */
 +        for (k = 0; k < fr->ntype && (has_vdw[j] == FALSE); k++)
 +        {
 +            /* We already checked that the atoms weren't perturbed,
 +             * so we only need to check state A now.
 +             */
 +            if (fr->bBHAM)
 +            {
 +                has_vdw[j] = (has_vdw[j] ||
 +                              (BHAMA(fr->nbfp, fr->ntype, tjA, k) != 0.0) ||
 +                              (BHAMB(fr->nbfp, fr->ntype, tjA, k) != 0.0) ||
 +                              (BHAMC(fr->nbfp, fr->ntype, tjA, k) != 0.0));
 +            }
 +            else
 +            {
 +                /* Standard LJ */
 +                has_vdw[j] = (has_vdw[j] ||
 +                              (C6(fr->nbfp, fr->ntype, tjA, k)  != 0.0) ||
 +                              (C12(fr->nbfp, fr->ntype, tjA, k) != 0.0));
 +            }
 +        }
 +    }
 +
 +    /* Now we know all we need to make the final check and assignment. */
 +    if (nj == 3)
 +    {
 +        /* So, is it an SPC?
 +         * For this we require thatn all atoms have charge,
 +         * the charges on atom 2 & 3 should be the same, and only
 +         * atom 1 might have VdW.
 +         */
 +        if (has_vdw[1] == FALSE &&
 +            has_vdw[2] == FALSE &&
 +            tmp_charge[0]  != 0 &&
 +            tmp_charge[1]  != 0 &&
 +            tmp_charge[2]  == tmp_charge[1])
 +        {
 +            srenew(solvent_parameters, *n_solvent_parameters+1);
 +            solvent_parameters[*n_solvent_parameters].model = esolSPC;
 +            solvent_parameters[*n_solvent_parameters].count = nmol;
 +            for (k = 0; k < 3; k++)
 +            {
 +                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 +                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 +            }
 +
 +            *cg_sp = *n_solvent_parameters;
 +            (*n_solvent_parameters)++;
 +        }
 +    }
 +    else if (nj == 4)
 +    {
 +        /* Or could it be a TIP4P?
 +         * For this we require thatn atoms 2,3,4 have charge, but not atom 1.
 +         * Only atom 1 mght have VdW.
 +         */
 +        if (has_vdw[1] == FALSE &&
 +            has_vdw[2] == FALSE &&
 +            has_vdw[3] == FALSE &&
 +            tmp_charge[0]  == 0 &&
 +            tmp_charge[1]  != 0 &&
 +            tmp_charge[2]  == tmp_charge[1] &&
 +            tmp_charge[3]  != 0)
 +        {
 +            srenew(solvent_parameters, *n_solvent_parameters+1);
 +            solvent_parameters[*n_solvent_parameters].model = esolTIP4P;
 +            solvent_parameters[*n_solvent_parameters].count = nmol;
 +            for (k = 0; k < 4; k++)
 +            {
 +                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 +                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 +            }
 +
 +            *cg_sp = *n_solvent_parameters;
 +            (*n_solvent_parameters)++;
 +        }
 +    }
 +
 +    *solvent_parameters_p = solvent_parameters;
 +}
 +
 +static void
 +check_solvent(FILE  *                fp,
 +              const gmx_mtop_t  *    mtop,
 +              t_forcerec  *          fr,
 +              cginfo_mb_t           *cginfo_mb)
 +{
 +    const t_block     *   cgs;
 +    const t_block     *   mols;
 +    const gmx_moltype_t  *molt;
 +    int                   mb, mol, cg_mol, at_offset, cg_offset, am, cgm, i, nmol_ch, nmol;
 +    int                   n_solvent_parameters;
 +    solvent_parameters_t *solvent_parameters;
 +    int                 **cg_sp;
 +    int                   bestsp, bestsol;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Going to determine what solvent types we have.\n");
 +    }
 +
 +    mols = &mtop->mols;
 +
 +    n_solvent_parameters = 0;
 +    solvent_parameters   = NULL;
 +    /* Allocate temporary array for solvent type */
 +    snew(cg_sp, mtop->nmolblock);
 +
 +    cg_offset = 0;
 +    at_offset = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +        cgs  = &molt->cgs;
 +        /* Here we have to loop over all individual molecules
 +         * because we need to check for QMMM particles.
 +         */
 +        snew(cg_sp[mb], cginfo_mb[mb].cg_mod);
 +        nmol_ch = cginfo_mb[mb].cg_mod/cgs->nr;
 +        nmol    = mtop->molblock[mb].nmol/nmol_ch;
 +        for (mol = 0; mol < nmol_ch; mol++)
 +        {
 +            cgm = mol*cgs->nr;
 +            am  = mol*cgs->index[cgs->nr];
 +            for (cg_mol = 0; cg_mol < cgs->nr; cg_mol++)
 +            {
 +                check_solvent_cg(molt, cg_mol, nmol,
 +                                 mtop->groups.grpnr[egcQMMM] ?
 +                                 mtop->groups.grpnr[egcQMMM]+at_offset+am : 0,
 +                                 &mtop->groups.grps[egcQMMM],
 +                                 fr,
 +                                 &n_solvent_parameters, &solvent_parameters,
 +                                 cginfo_mb[mb].cginfo[cgm+cg_mol],
 +                                 &cg_sp[mb][cgm+cg_mol]);
 +            }
 +        }
 +        cg_offset += cgs->nr;
 +        at_offset += cgs->index[cgs->nr];
 +    }
 +
 +    /* Puh! We finished going through all charge groups.
 +     * Now find the most common solvent model.
 +     */
 +
 +    /* Most common solvent this far */
 +    bestsp = -2;
 +    for (i = 0; i < n_solvent_parameters; i++)
 +    {
 +        if (bestsp == -2 ||
 +            solvent_parameters[i].count > solvent_parameters[bestsp].count)
 +        {
 +            bestsp = i;
 +        }
 +    }
 +
 +    if (bestsp >= 0)
 +    {
 +        bestsol = solvent_parameters[bestsp].model;
 +    }
 +    else
 +    {
 +        bestsol = esolNO;
 +    }
 +
 +#ifdef DISABLE_WATER_NLIST
 +    bestsol = esolNO;
 +#endif
 +
 +    fr->nWatMol = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        cgs  = &mtop->moltype[mtop->molblock[mb].type].cgs;
 +        nmol = (mtop->molblock[mb].nmol*cgs->nr)/cginfo_mb[mb].cg_mod;
 +        for (i = 0; i < cginfo_mb[mb].cg_mod; i++)
 +        {
 +            if (cg_sp[mb][i] == bestsp)
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], bestsol);
 +                fr->nWatMol += nmol;
 +            }
 +            else
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], esolNO);
 +            }
 +        }
 +        sfree(cg_sp[mb]);
 +    }
 +    sfree(cg_sp);
 +
 +    if (bestsol != esolNO && fp != NULL)
 +    {
 +        fprintf(fp, "\nEnabling %s-like water optimization for %d molecules.\n\n",
 +                esol_names[bestsol],
 +                solvent_parameters[bestsp].count);
 +    }
 +
 +    sfree(solvent_parameters);
 +    fr->solvent_opt = bestsol;
 +}
 +
 +enum {
 +    acNONE = 0, acCONSTRAINT, acSETTLE
 +};
 +
 +static cginfo_mb_t *init_cginfo_mb(FILE *fplog, const gmx_mtop_t *mtop,
 +                                   t_forcerec *fr, gmx_bool bNoSolvOpt,
 +                                   gmx_bool *bFEP_NonBonded,
 +                                   gmx_bool *bExcl_IntraCGAll_InterCGNone)
 +{
 +    const t_block        *cgs;
 +    const t_blocka       *excl;
 +    const gmx_moltype_t  *molt;
 +    const gmx_molblock_t *molb;
 +    cginfo_mb_t          *cginfo_mb;
 +    gmx_bool             *type_VDW;
 +    int                  *cginfo;
 +    int                   cg_offset, a_offset, cgm, am;
 +    int                   mb, m, ncg_tot, cg, a0, a1, gid, ai, j, aj, excl_nalloc;
 +    int                  *a_con;
 +    int                   ftype;
 +    int                   ia;
 +    gmx_bool              bId, *bExcl, bExclIntraAll, bExclInter, bHaveVDW, bHaveQ, bHavePerturbedAtoms;
 +
 +    ncg_tot = ncg_mtop(mtop);
 +    snew(cginfo_mb, mtop->nmolblock);
 +
 +    snew(type_VDW, fr->ntype);
 +    for (ai = 0; ai < fr->ntype; ai++)
 +    {
 +        type_VDW[ai] = FALSE;
 +        for (j = 0; j < fr->ntype; j++)
 +        {
 +            type_VDW[ai] = type_VDW[ai] ||
 +                fr->bBHAM ||
 +                C6(fr->nbfp, fr->ntype, ai, j) != 0 ||
 +                C12(fr->nbfp, fr->ntype, ai, j) != 0;
 +        }
 +    }
 +
 +    *bFEP_NonBonded               = FALSE;
 +    *bExcl_IntraCGAll_InterCGNone = TRUE;
 +
 +    excl_nalloc = 10;
 +    snew(bExcl, excl_nalloc);
 +    cg_offset = 0;
 +    a_offset  = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        molt = &mtop->moltype[molb->type];
 +        cgs  = &molt->cgs;
 +        excl = &molt->excls;
 +
 +        /* Check if the cginfo is identical for all molecules in this block.
 +         * If so, we only need an array of the size of one molecule.
 +         * Otherwise we make an array of #mol times #cgs per molecule.
 +         */
 +        bId = TRUE;
 +        am  = 0;
 +        for (m = 0; m < molb->nmol; m++)
 +        {
 +            am = m*cgs->index[cgs->nr];
 +            for (cg = 0; cg < cgs->nr; cg++)
 +            {
 +                a0 = cgs->index[cg];
 +                a1 = cgs->index[cg+1];
 +                if (ggrpnr(&mtop->groups, egcENER, a_offset+am+a0) !=
 +                    ggrpnr(&mtop->groups, egcENER, a_offset   +a0))
 +                {
 +                    bId = FALSE;
 +                }
 +                if (mtop->groups.grpnr[egcQMMM] != NULL)
 +                {
 +                    for (ai = a0; ai < a1; ai++)
 +                    {
 +                        if (mtop->groups.grpnr[egcQMMM][a_offset+am+ai] !=
 +                            mtop->groups.grpnr[egcQMMM][a_offset   +ai])
 +                        {
 +                            bId = FALSE;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        cginfo_mb[mb].cg_start = cg_offset;
 +        cginfo_mb[mb].cg_end   = cg_offset + molb->nmol*cgs->nr;
 +        cginfo_mb[mb].cg_mod   = (bId ? 1 : molb->nmol)*cgs->nr;
 +        snew(cginfo_mb[mb].cginfo, cginfo_mb[mb].cg_mod);
 +        cginfo = cginfo_mb[mb].cginfo;
 +
 +        /* Set constraints flags for constrained atoms */
 +        snew(a_con, molt->atoms.nr);
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (interaction_function[ftype].flags & IF_CONSTRAINT)
 +            {
 +                int nral;
 +
 +                nral = NRAL(ftype);
 +                for (ia = 0; ia < molt->ilist[ftype].nr; ia += 1+nral)
 +                {
 +                    int a;
 +
 +                    for (a = 0; a < nral; a++)
 +                    {
 +                        a_con[molt->ilist[ftype].iatoms[ia+1+a]] =
 +                            (ftype == F_SETTLE ? acSETTLE : acCONSTRAINT);
 +                    }
 +                }
 +            }
 +        }
 +
 +        for (m = 0; m < (bId ? 1 : molb->nmol); m++)
 +        {
 +            cgm = m*cgs->nr;
 +            am  = m*cgs->index[cgs->nr];
 +            for (cg = 0; cg < cgs->nr; cg++)
 +            {
 +                a0 = cgs->index[cg];
 +                a1 = cgs->index[cg+1];
 +
 +                /* Store the energy group in cginfo */
 +                gid = ggrpnr(&mtop->groups, egcENER, a_offset+am+a0);
 +                SET_CGINFO_GID(cginfo[cgm+cg], gid);
 +
 +                /* Check the intra/inter charge group exclusions */
 +                if (a1-a0 > excl_nalloc)
 +                {
 +                    excl_nalloc = a1 - a0;
 +                    srenew(bExcl, excl_nalloc);
 +                }
 +                /* bExclIntraAll: all intra cg interactions excluded
 +                 * bExclInter:    any inter cg interactions excluded
 +                 */
 +                bExclIntraAll       = TRUE;
 +                bExclInter          = FALSE;
 +                bHaveVDW            = FALSE;
 +                bHaveQ              = FALSE;
 +                bHavePerturbedAtoms = FALSE;
 +                for (ai = a0; ai < a1; ai++)
 +                {
 +                    /* Check VDW and electrostatic interactions */
 +                    bHaveVDW = bHaveVDW || (type_VDW[molt->atoms.atom[ai].type] ||
 +                                            type_VDW[molt->atoms.atom[ai].typeB]);
 +                    bHaveQ  = bHaveQ    || (molt->atoms.atom[ai].q != 0 ||
 +                                            molt->atoms.atom[ai].qB != 0);
 +
 +                    bHavePerturbedAtoms = bHavePerturbedAtoms || (PERTURBED(molt->atoms.atom[ai]) != 0);
 +
 +                    /* Clear the exclusion list for atom ai */
 +                    for (aj = a0; aj < a1; aj++)
 +                    {
 +                        bExcl[aj-a0] = FALSE;
 +                    }
 +                    /* Loop over all the exclusions of atom ai */
 +                    for (j = excl->index[ai]; j < excl->index[ai+1]; j++)
 +                    {
 +                        aj = excl->a[j];
 +                        if (aj < a0 || aj >= a1)
 +                        {
 +                            bExclInter = TRUE;
 +                        }
 +                        else
 +                        {
 +                            bExcl[aj-a0] = TRUE;
 +                        }
 +                    }
 +                    /* Check if ai excludes a0 to a1 */
 +                    for (aj = a0; aj < a1; aj++)
 +                    {
 +                        if (!bExcl[aj-a0])
 +                        {
 +                            bExclIntraAll = FALSE;
 +                        }
 +                    }
 +
 +                    switch (a_con[ai])
 +                    {
 +                        case acCONSTRAINT:
 +                            SET_CGINFO_CONSTR(cginfo[cgm+cg]);
 +                            break;
 +                        case acSETTLE:
 +                            SET_CGINFO_SETTLE(cginfo[cgm+cg]);
 +                            break;
 +                        default:
 +                            break;
 +                    }
 +                }
 +                if (bExclIntraAll)
 +                {
 +                    SET_CGINFO_EXCL_INTRA(cginfo[cgm+cg]);
 +                }
 +                if (bExclInter)
 +                {
 +                    SET_CGINFO_EXCL_INTER(cginfo[cgm+cg]);
 +                }
 +                if (a1 - a0 > MAX_CHARGEGROUP_SIZE)
 +                {
 +                    /* The size in cginfo is currently only read with DD */
 +                    gmx_fatal(FARGS, "A charge group has size %d which is larger than the limit of %d atoms", a1-a0, MAX_CHARGEGROUP_SIZE);
 +                }
 +                if (bHaveVDW)
 +                {
 +                    SET_CGINFO_HAS_VDW(cginfo[cgm+cg]);
 +                }
 +                if (bHaveQ)
 +                {
 +                    SET_CGINFO_HAS_Q(cginfo[cgm+cg]);
 +                }
 +                if (bHavePerturbedAtoms && fr->efep != efepNO)
 +                {
 +                    SET_CGINFO_FEP(cginfo[cgm+cg]);
 +                    *bFEP_NonBonded = TRUE;
 +                }
 +                /* Store the charge group size */
 +                SET_CGINFO_NATOMS(cginfo[cgm+cg], a1-a0);
 +
 +                if (!bExclIntraAll || bExclInter)
 +                {
 +                    *bExcl_IntraCGAll_InterCGNone = FALSE;
 +                }
 +            }
 +        }
 +
 +        sfree(a_con);
 +
 +        cg_offset += molb->nmol*cgs->nr;
 +        a_offset  += molb->nmol*cgs->index[cgs->nr];
 +    }
 +    sfree(bExcl);
 +
 +    /* the solvent optimizer is called after the QM is initialized,
 +     * because we don't want to have the QM subsystemto become an
 +     * optimized solvent
 +     */
 +
 +    check_solvent(fplog, mtop, fr, cginfo_mb);
 +
 +    if (getenv("GMX_NO_SOLV_OPT"))
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Found environment variable GMX_NO_SOLV_OPT.\n"
 +                    "Disabling all solvent optimization\n");
 +        }
 +        fr->solvent_opt = esolNO;
 +    }
 +    if (bNoSolvOpt)
 +    {
 +        fr->solvent_opt = esolNO;
 +    }
 +    if (!fr->solvent_opt)
 +    {
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            for (cg = 0; cg < cginfo_mb[mb].cg_mod; cg++)
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[cg], esolNO);
 +            }
 +        }
 +    }
 +
 +    return cginfo_mb;
 +}
 +
 +static int *cginfo_expand(int nmb, cginfo_mb_t *cgi_mb)
 +{
 +    int  ncg, mb, cg;
 +    int *cginfo;
 +
 +    ncg = cgi_mb[nmb-1].cg_end;
 +    snew(cginfo, ncg);
 +    mb = 0;
 +    for (cg = 0; cg < ncg; cg++)
 +    {
 +        while (cg >= cgi_mb[mb].cg_end)
 +        {
 +            mb++;
 +        }
 +        cginfo[cg] =
 +            cgi_mb[mb].cginfo[(cg - cgi_mb[mb].cg_start) % cgi_mb[mb].cg_mod];
 +    }
 +
 +    return cginfo;
 +}
 +
 +static void set_chargesum(FILE *log, t_forcerec *fr, const gmx_mtop_t *mtop)
 +{
 +    /*This now calculates sum for q and c6*/
 +    double         qsum, q2sum, q, c6sum, c6;
 +    int            mb, nmol, i;
 +    const t_atoms *atoms;
 +
 +    qsum   = 0;
 +    q2sum  = 0;
 +    c6sum  = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        nmol  = mtop->molblock[mb].nmol;
 +        atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +        for (i = 0; i < atoms->nr; i++)
 +        {
 +            q       = atoms->atom[i].q;
 +            qsum   += nmol*q;
 +            q2sum  += nmol*q*q;
 +            c6      = mtop->ffparams.iparams[atoms->atom[i].type*(mtop->ffparams.atnr+1)].lj.c6;
 +            c6sum  += nmol*c6;
 +        }
 +    }
 +    fr->qsum[0]   = qsum;
 +    fr->q2sum[0]  = q2sum;
 +    fr->c6sum[0]  = c6sum;
 +
 +    if (fr->efep != efepNO)
 +    {
 +        qsum   = 0;
 +        q2sum  = 0;
 +        c6sum  = 0;
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            nmol  = mtop->molblock[mb].nmol;
 +            atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +            for (i = 0; i < atoms->nr; i++)
 +            {
 +                q       = atoms->atom[i].qB;
 +                qsum   += nmol*q;
 +                q2sum  += nmol*q*q;
 +                c6      = mtop->ffparams.iparams[atoms->atom[i].typeB*(mtop->ffparams.atnr+1)].lj.c6;
 +                c6sum  += nmol*c6;
 +            }
 +            fr->qsum[1]   = qsum;
 +            fr->q2sum[1]  = q2sum;
 +            fr->c6sum[1]  = c6sum;
 +        }
 +    }
 +    else
 +    {
 +        fr->qsum[1]   = fr->qsum[0];
 +        fr->q2sum[1]  = fr->q2sum[0];
 +        fr->c6sum[1]  = fr->c6sum[0];
 +    }
 +    if (log)
 +    {
 +        if (fr->efep == efepNO)
 +        {
 +            fprintf(log, "System total charge: %.3f\n", fr->qsum[0]);
 +        }
 +        else
 +        {
 +            fprintf(log, "System total charge, top. A: %.3f top. B: %.3f\n",
 +                    fr->qsum[0], fr->qsum[1]);
 +        }
 +    }
 +}
 +
 +void update_forcerec(t_forcerec *fr, matrix box)
 +{
 +    if (fr->eeltype == eelGRF)
 +    {
 +        calc_rffac(NULL, fr->eeltype, fr->epsilon_r, fr->epsilon_rf,
 +                   fr->rcoulomb, fr->temp, fr->zsquare, box,
 +                   &fr->kappa, &fr->k_rf, &fr->c_rf);
 +    }
 +}
 +
 +void set_avcsixtwelve(FILE *fplog, t_forcerec *fr, const gmx_mtop_t *mtop)
 +{
 +    const t_atoms  *atoms, *atoms_tpi;
 +    const t_blocka *excl;
 +    int             mb, nmol, nmolc, i, j, tpi, tpj, j1, j2, k, n, nexcl, q;
 +    gmx_int64_t     npair, npair_ij, tmpi, tmpj;
 +    double          csix, ctwelve;
 +    int             ntp, *typecount;
 +    gmx_bool        bBHAM;
 +    real           *nbfp;
 +    real           *nbfp_comb = NULL;
 +
 +    ntp   = fr->ntype;
 +    bBHAM = fr->bBHAM;
 +    nbfp  = fr->nbfp;
 +
 +    /* For LJ-PME, we want to correct for the difference between the
 +     * actual C6 values and the C6 values used by the LJ-PME based on
 +     * combination rules. */
 +
 +    if (EVDW_PME(fr->vdwtype))
 +    {
 +        nbfp_comb = mk_nbfp_combination_rule(&mtop->ffparams,
 +                                             (fr->ljpme_combination_rule == eljpmeLB) ? eCOMB_ARITHMETIC : eCOMB_GEOMETRIC);
 +        for (tpi = 0; tpi < ntp; ++tpi)
 +        {
 +            for (tpj = 0; tpj < ntp; ++tpj)
 +            {
 +                C6(nbfp_comb, ntp, tpi, tpj) =
 +                    C6(nbfp, ntp, tpi, tpj) - C6(nbfp_comb, ntp, tpi, tpj);
 +                C12(nbfp_comb, ntp, tpi, tpj) = C12(nbfp, ntp, tpi, tpj);
 +            }
 +        }
 +        nbfp = nbfp_comb;
 +    }
 +    for (q = 0; q < (fr->efep == efepNO ? 1 : 2); q++)
 +    {
 +        csix    = 0;
 +        ctwelve = 0;
 +        npair   = 0;
 +        nexcl   = 0;
 +        if (!fr->n_tpi)
 +        {
 +            /* Count the types so we avoid natoms^2 operations */
 +            snew(typecount, ntp);
 +            gmx_mtop_count_atomtypes(mtop, q, typecount);
 +
 +            for (tpi = 0; tpi < ntp; tpi++)
 +            {
 +                for (tpj = tpi; tpj < ntp; tpj++)
 +                {
 +                    tmpi = typecount[tpi];
 +                    tmpj = typecount[tpj];
 +                    if (tpi != tpj)
 +                    {
 +                        npair_ij = tmpi*tmpj;
 +                    }
 +                    else
 +                    {
 +                        npair_ij = tmpi*(tmpi - 1)/2;
 +                    }
 +                    if (bBHAM)
 +                    {
 +                        /* nbfp now includes the 6.0 derivative prefactor */
 +                        csix    += npair_ij*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 +                    }
 +                    else
 +                    {
 +                        /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                        csix    += npair_ij*   C6(nbfp, ntp, tpi, tpj)/6.0;
 +                        ctwelve += npair_ij*  C12(nbfp, ntp, tpi, tpj)/12.0;
 +                    }
 +                    npair += npair_ij;
 +                }
 +            }
 +            sfree(typecount);
 +            /* Subtract the excluded pairs.
 +             * The main reason for substracting exclusions is that in some cases
 +             * some combinations might never occur and the parameters could have
 +             * any value. These unused values should not influence the dispersion
 +             * correction.
 +             */
 +            for (mb = 0; mb < mtop->nmolblock; mb++)
 +            {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                excl  = &mtop->moltype[mtop->molblock[mb].type].excls;
 +                for (i = 0; (i < atoms->nr); i++)
 +                {
 +                    if (q == 0)
 +                    {
 +                        tpi = atoms->atom[i].type;
 +                    }
 +                    else
 +                    {
 +                        tpi = atoms->atom[i].typeB;
 +                    }
 +                    j1  = excl->index[i];
 +                    j2  = excl->index[i+1];
 +                    for (j = j1; j < j2; j++)
 +                    {
 +                        k = excl->a[j];
 +                        if (k > i)
 +                        {
 +                            if (q == 0)
 +                            {
 +                                tpj = atoms->atom[k].type;
 +                            }
 +                            else
 +                            {
 +                                tpj = atoms->atom[k].typeB;
 +                            }
 +                            if (bBHAM)
 +                            {
 +                                /* nbfp now includes the 6.0 derivative prefactor */
 +                                csix -= nmol*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 +                            }
 +                            else
 +                            {
 +                                /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                                csix    -= nmol*C6 (nbfp, ntp, tpi, tpj)/6.0;
 +                                ctwelve -= nmol*C12(nbfp, ntp, tpi, tpj)/12.0;
 +                            }
 +                            nexcl += nmol;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            /* Only correct for the interaction of the test particle
 +             * with the rest of the system.
 +             */
 +            atoms_tpi =
 +                &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].atoms;
 +
 +            npair = 0;
 +            for (mb = 0; mb < mtop->nmolblock; mb++)
 +            {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                for (j = 0; j < atoms->nr; j++)
 +                {
 +                    nmolc = nmol;
 +                    /* Remove the interaction of the test charge group
 +                     * with itself.
 +                     */
 +                    if (mb == mtop->nmolblock-1)
 +                    {
 +                        nmolc--;
 +
 +                        if (mb == 0 && nmol == 1)
 +                        {
 +                            gmx_fatal(FARGS, "Old format tpr with TPI, please generate a new tpr file");
 +                        }
 +                    }
 +                    if (q == 0)
 +                    {
 +                        tpj = atoms->atom[j].type;
 +                    }
 +                    else
 +                    {
 +                        tpj = atoms->atom[j].typeB;
 +                    }
 +                    for (i = 0; i < fr->n_tpi; i++)
 +                    {
 +                        if (q == 0)
 +                        {
 +                            tpi = atoms_tpi->atom[i].type;
 +                        }
 +                        else
 +                        {
 +                            tpi = atoms_tpi->atom[i].typeB;
 +                        }
 +                        if (bBHAM)
 +                        {
 +                            /* nbfp now includes the 6.0 derivative prefactor */
 +                            csix    += nmolc*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 +                        }
 +                        else
 +                        {
 +                            /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                            csix    += nmolc*C6 (nbfp, ntp, tpi, tpj)/6.0;
 +                            ctwelve += nmolc*C12(nbfp, ntp, tpi, tpj)/12.0;
 +                        }
 +                        npair += nmolc;
 +                    }
 +                }
 +            }
 +        }
 +        if (npair - nexcl <= 0 && fplog)
 +        {
 +            fprintf(fplog, "\nWARNING: There are no atom pairs for dispersion correction\n\n");
 +            csix     = 0;
 +            ctwelve  = 0;
 +        }
 +        else
 +        {
 +            csix    /= npair - nexcl;
 +            ctwelve /= npair - nexcl;
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Counted %d exclusions\n", nexcl);
 +            fprintf(debug, "Average C6 parameter is: %10g\n", (double)csix);
 +            fprintf(debug, "Average C12 parameter is: %10g\n", (double)ctwelve);
 +        }
 +        fr->avcsix[q]    = csix;
 +        fr->avctwelve[q] = ctwelve;
 +    }
 +
 +    if (EVDW_PME(fr->vdwtype))
 +    {
 +        sfree(nbfp_comb);
 +    }
 +
 +    if (fplog != NULL)
 +    {
 +        if (fr->eDispCorr == edispcAllEner ||
 +            fr->eDispCorr == edispcAllEnerPres)
 +        {
 +            fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
 +                    fr->avcsix[0], fr->avctwelve[0]);
 +        }
 +        else
 +        {
 +            fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e\n", fr->avcsix[0]);
 +        }
 +    }
 +}
 +
 +
 +static void set_bham_b_max(FILE *fplog, t_forcerec *fr,
 +                           const gmx_mtop_t *mtop)
 +{
 +    const t_atoms *at1, *at2;
 +    int            mt1, mt2, i, j, tpi, tpj, ntypes;
 +    real           b, bmin;
 +    real          *nbfp;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Determining largest Buckingham b parameter for table\n");
 +    }
 +    nbfp   = fr->nbfp;
 +    ntypes = fr->ntype;
 +
 +    bmin           = -1;
 +    fr->bham_b_max = 0;
 +    for (mt1 = 0; mt1 < mtop->nmoltype; mt1++)
 +    {
 +        at1 = &mtop->moltype[mt1].atoms;
 +        for (i = 0; (i < at1->nr); i++)
 +        {
 +            tpi = at1->atom[i].type;
 +            if (tpi >= ntypes)
 +            {
 +                gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", i, tpi, ntypes);
 +            }
 +
 +            for (mt2 = mt1; mt2 < mtop->nmoltype; mt2++)
 +            {
 +                at2 = &mtop->moltype[mt2].atoms;
 +                for (j = 0; (j < at2->nr); j++)
 +                {
 +                    tpj = at2->atom[j].type;
 +                    if (tpj >= ntypes)
 +                    {
 +                        gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", j, tpj, ntypes);
 +                    }
 +                    b = BHAMB(nbfp, ntypes, tpi, tpj);
 +                    if (b > fr->bham_b_max)
 +                    {
 +                        fr->bham_b_max = b;
 +                    }
 +                    if ((b < bmin) || (bmin == -1))
 +                    {
 +                        bmin = b;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Buckingham b parameters, min: %g, max: %g\n",
 +                bmin, fr->bham_b_max);
 +    }
 +}
 +
 +static void make_nbf_tables(FILE *fp, const output_env_t oenv,
 +                            t_forcerec *fr, real rtab,
 +                            const t_commrec *cr,
 +                            const char *tabfn, char *eg1, char *eg2,
 +                            t_nblists *nbl)
 +{
 +    char buf[STRLEN];
 +    int  i, j;
 +
 +    if (tabfn == NULL)
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "No table file name passed, can not read table, can not do non-bonded interactions\n");
 +        }
 +        return;
 +    }
 +
 +    sprintf(buf, "%s", tabfn);
 +    if (eg1 && eg2)
 +    {
 +        /* Append the two energy group names */
 +        sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s",
 +                eg1, eg2, ftp2ext(efXVG));
 +    }
 +    nbl->table_elec_vdw = make_tables(fp, oenv, fr, MASTER(cr), buf, rtab, 0);
 +    /* Copy the contents of the table to separate coulomb and LJ tables too,
 +     * to improve cache performance.
 +     */
 +    /* For performance reasons we want
 +     * the table data to be aligned to 16-byte. The pointers could be freed
 +     * but currently aren't.
 +     */
 +    nbl->table_elec.interaction   = GMX_TABLE_INTERACTION_ELEC;
 +    nbl->table_elec.format        = nbl->table_elec_vdw.format;
 +    nbl->table_elec.r             = nbl->table_elec_vdw.r;
 +    nbl->table_elec.n             = nbl->table_elec_vdw.n;
 +    nbl->table_elec.scale         = nbl->table_elec_vdw.scale;
 +    nbl->table_elec.scale_exp     = nbl->table_elec_vdw.scale_exp;
 +    nbl->table_elec.formatsize    = nbl->table_elec_vdw.formatsize;
 +    nbl->table_elec.ninteractions = 1;
 +    nbl->table_elec.stride        = nbl->table_elec.formatsize * nbl->table_elec.ninteractions;
 +    snew_aligned(nbl->table_elec.data, nbl->table_elec.stride*(nbl->table_elec.n+1), 32);
 +
 +    nbl->table_vdw.interaction   = GMX_TABLE_INTERACTION_VDWREP_VDWDISP;
 +    nbl->table_vdw.format        = nbl->table_elec_vdw.format;
 +    nbl->table_vdw.r             = nbl->table_elec_vdw.r;
 +    nbl->table_vdw.n             = nbl->table_elec_vdw.n;
 +    nbl->table_vdw.scale         = nbl->table_elec_vdw.scale;
 +    nbl->table_vdw.scale_exp     = nbl->table_elec_vdw.scale_exp;
 +    nbl->table_vdw.formatsize    = nbl->table_elec_vdw.formatsize;
 +    nbl->table_vdw.ninteractions = 2;
 +    nbl->table_vdw.stride        = nbl->table_vdw.formatsize * nbl->table_vdw.ninteractions;
 +    snew_aligned(nbl->table_vdw.data, nbl->table_vdw.stride*(nbl->table_vdw.n+1), 32);
 +
 +    for (i = 0; i <= nbl->table_elec_vdw.n; i++)
 +    {
 +        for (j = 0; j < 4; j++)
 +        {
 +            nbl->table_elec.data[4*i+j] = nbl->table_elec_vdw.data[12*i+j];
 +        }
 +        for (j = 0; j < 8; j++)
 +        {
 +            nbl->table_vdw.data[8*i+j] = nbl->table_elec_vdw.data[12*i+4+j];
 +        }
 +    }
 +}
 +
 +static void count_tables(int ftype1, int ftype2, const gmx_mtop_t *mtop,
 +                         int *ncount, int **count)
 +{
 +    const gmx_moltype_t *molt;
 +    const t_ilist       *il;
 +    int                  mt, ftype, stride, i, j, tabnr;
 +
 +    for (mt = 0; mt < mtop->nmoltype; mt++)
 +    {
 +        molt = &mtop->moltype[mt];
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (ftype == ftype1 || ftype == ftype2)
 +            {
 +                il     = &molt->ilist[ftype];
 +                stride = 1 + NRAL(ftype);
 +                for (i = 0; i < il->nr; i += stride)
 +                {
 +                    tabnr = mtop->ffparams.iparams[il->iatoms[i]].tab.table;
 +                    if (tabnr < 0)
 +                    {
 +                        gmx_fatal(FARGS, "A bonded table number is smaller than 0: %d\n", tabnr);
 +                    }
 +                    if (tabnr >= *ncount)
 +                    {
 +                        srenew(*count, tabnr+1);
 +                        for (j = *ncount; j < tabnr+1; j++)
 +                        {
 +                            (*count)[j] = 0;
 +                        }
 +                        *ncount = tabnr+1;
 +                    }
 +                    (*count)[tabnr]++;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static bondedtable_t *make_bonded_tables(FILE *fplog,
 +                                         int ftype1, int ftype2,
 +                                         const gmx_mtop_t *mtop,
 +                                         const char *basefn, const char *tabext)
 +{
 +    int            i, ncount, *count;
 +    char           tabfn[STRLEN];
 +    bondedtable_t *tab;
 +
 +    tab = NULL;
 +
 +    ncount = 0;
 +    count  = NULL;
 +    count_tables(ftype1, ftype2, mtop, &ncount, &count);
 +
 +    if (ncount > 0)
 +    {
 +        snew(tab, ncount);
 +        for (i = 0; i < ncount; i++)
 +        {
 +            if (count[i] > 0)
 +            {
 +                sprintf(tabfn, "%s", basefn);
 +                sprintf(tabfn + strlen(basefn) - strlen(ftp2ext(efXVG)) - 1, "_%s%d.%s",
 +                        tabext, i, ftp2ext(efXVG));
 +                tab[i] = make_bonded_table(fplog, tabfn, NRAL(ftype1)-2);
 +            }
 +        }
 +        sfree(count);
 +    }
 +
 +    return tab;
 +}
 +
 +void forcerec_set_ranges(t_forcerec *fr,
 +                         int ncg_home, int ncg_force,
 +                         int natoms_force,
 +                         int natoms_force_constr, int natoms_f_novirsum)
 +{
 +    fr->cg0 = 0;
 +    fr->hcg = ncg_home;
 +
 +    /* fr->ncg_force is unused in the standard code,
 +     * but it can be useful for modified code dealing with charge groups.
 +     */
 +    fr->ncg_force           = ncg_force;
 +    fr->natoms_force        = natoms_force;
 +    fr->natoms_force_constr = natoms_force_constr;
 +
 +    if (fr->natoms_force_constr > fr->nalloc_force)
 +    {
 +        fr->nalloc_force = over_alloc_dd(fr->natoms_force_constr);
 +
 +        if (fr->bTwinRange)
 +        {
 +            srenew(fr->f_twin, fr->nalloc_force);
 +        }
 +    }
 +
 +    if (fr->bF_NoVirSum)
 +    {
 +        fr->f_novirsum_n = natoms_f_novirsum;
 +        if (fr->f_novirsum_n > fr->f_novirsum_nalloc)
 +        {
 +            fr->f_novirsum_nalloc = over_alloc_dd(fr->f_novirsum_n);
 +            srenew(fr->f_novirsum_alloc, fr->f_novirsum_nalloc);
 +        }
 +    }
 +    else
 +    {
 +        fr->f_novirsum_n = 0;
 +    }
 +}
 +
 +static real cutoff_inf(real cutoff)
 +{
 +    if (cutoff == 0)
 +    {
 +        cutoff = GMX_CUTOFF_INF;
 +    }
 +
 +    return cutoff;
 +}
 +
 +static void make_adress_tf_tables(FILE *fp, const output_env_t oenv,
 +                                  t_forcerec *fr, const t_inputrec *ir,
 +                                  const char *tabfn, const gmx_mtop_t *mtop,
 +                                  matrix     box)
 +{
 +    char buf[STRLEN];
 +    int  i, j;
 +
 +    if (tabfn == NULL)
 +    {
 +        gmx_fatal(FARGS, "No thermoforce table file given. Use -tabletf to specify a file\n");
 +        return;
 +    }
 +
 +    snew(fr->atf_tabs, ir->adress->n_tf_grps);
 +
 +    sprintf(buf, "%s", tabfn);
 +    for (i = 0; i < ir->adress->n_tf_grps; i++)
 +    {
 +        j = ir->adress->tf_table_index[i]; /* get energy group index */
 +        sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "tf_%s.%s",
 +                *(mtop->groups.grpname[mtop->groups.grps[egcENER].nm_ind[j]]), ftp2ext(efXVG));
 +        if (fp)
 +        {
 +            fprintf(fp, "loading tf table for energygrp index %d from %s\n", ir->adress->tf_table_index[i], buf);
 +        }
 +        fr->atf_tabs[i] = make_atf_table(fp, oenv, fr, buf, box);
 +    }
 +
 +}
 +
 +gmx_bool can_use_allvsall(const t_inputrec *ir, gmx_bool bPrintNote, t_commrec *cr, FILE *fp)
 +{
 +    gmx_bool bAllvsAll;
 +
 +    bAllvsAll =
 +        (
 +            ir->rlist == 0            &&
 +            ir->rcoulomb == 0         &&
 +            ir->rvdw == 0             &&
 +            ir->ePBC == epbcNONE      &&
 +            ir->vdwtype == evdwCUT    &&
 +            ir->coulombtype == eelCUT &&
 +            ir->efep == efepNO        &&
 +            (ir->implicit_solvent == eisNO ||
 +             (ir->implicit_solvent == eisGBSA && (ir->gb_algorithm == egbSTILL ||
 +                                                  ir->gb_algorithm == egbHCT   ||
 +                                                  ir->gb_algorithm == egbOBC))) &&
 +            getenv("GMX_NO_ALLVSALL") == NULL
 +        );
 +
 +    if (bAllvsAll && ir->opts.ngener > 1)
 +    {
 +        const char *note = "NOTE: Can not use all-vs-all force loops, because there are multiple energy monitor groups; you might get significantly higher performance when using only a single energy monitor group.\n";
 +
 +        if (bPrintNote)
 +        {
 +            if (MASTER(cr))
 +            {
 +                fprintf(stderr, "\n%s\n", note);
 +            }
 +            if (fp != NULL)
 +            {
 +                fprintf(fp, "\n%s\n", note);
 +            }
 +        }
 +        bAllvsAll = FALSE;
 +    }
 +
 +    if (bAllvsAll && fp && MASTER(cr))
 +    {
 +        fprintf(fp, "\nUsing SIMD all-vs-all kernels.\n\n");
 +    }
 +
 +    return bAllvsAll;
 +}
 +
 +
 +static void init_forcerec_f_threads(t_forcerec *fr, int nenergrp)
 +{
 +    int t, i;
 +
 +    /* These thread local data structures are used for bondeds only */
 +    fr->nthreads = gmx_omp_nthreads_get(emntBonded);
 +
 +    if (fr->nthreads > 1)
 +    {
 +        snew(fr->f_t, fr->nthreads);
 +        /* Thread 0 uses the global force and energy arrays */
 +        for (t = 1; t < fr->nthreads; t++)
 +        {
 +            fr->f_t[t].f        = NULL;
 +            fr->f_t[t].f_nalloc = 0;
 +            snew(fr->f_t[t].fshift, SHIFTS);
 +            fr->f_t[t].grpp.nener = nenergrp*nenergrp;
 +            for (i = 0; i < egNR; i++)
 +            {
 +                snew(fr->f_t[t].grpp.ener[i], fr->f_t[t].grpp.nener);
 +            }
 +        }
 +    }
 +}
 +
 +
 +gmx_bool nbnxn_acceleration_supported(FILE             *fplog,
 +                                      const t_commrec  *cr,
 +                                      const t_inputrec *ir,
 +                                      gmx_bool          bGPU)
 +{
 +    if (!bGPU && (ir->vdwtype == evdwPME && ir->ljpme_combination_rule == eljpmeLB))
 +    {
 +        md_print_warn(cr, fplog, "LJ-PME with Lorentz-Berthelot is not supported with %s, falling back to %s\n",
 +                      bGPU ? "GPUs" : "SIMD kernels",
 +                      bGPU ? "CPU only" : "plain-C kernels");
 +        return FALSE;
 +    }
 +
 +    return TRUE;
 +}
 +
 +
 +static void pick_nbnxn_kernel_cpu(const t_inputrec gmx_unused *ir,
 +                                  int                         *kernel_type,
 +                                  int                         *ewald_excl)
 +{
 +    *kernel_type = nbnxnk4x4_PlainC;
 +    *ewald_excl  = ewaldexclTable;
 +
 +#ifdef GMX_NBNXN_SIMD
 +    {
 +#ifdef GMX_NBNXN_SIMD_4XN
 +        *kernel_type = nbnxnk4xN_SIMD_4xN;
 +#endif
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +        *kernel_type = nbnxnk4xN_SIMD_2xNN;
 +#endif
 +
 +#if defined GMX_NBNXN_SIMD_2XNN && defined GMX_NBNXN_SIMD_4XN
 +        /* We need to choose if we want 2x(N+N) or 4xN kernels.
 +         * Currently this is based on the SIMD acceleration choice,
 +         * but it might be better to decide this at runtime based on CPU.
 +         *
 +         * 4xN calculates more (zero) interactions, but has less pair-search
 +         * work and much better kernel instruction scheduling.
 +         *
 +         * Up till now we have only seen that on Intel Sandy/Ivy Bridge,
 +         * which doesn't have FMA, both the analytical and tabulated Ewald
 +         * kernels have similar pair rates for 4x8 and 2x(4+4), so we choose
 +         * 2x(4+4) because it results in significantly fewer pairs.
 +         * For RF, the raw pair rate of the 4x8 kernel is higher than 2x(4+4),
 +         * 10% with HT, 50% without HT. As we currently don't detect the actual
 +         * use of HT, use 4x8 to avoid a potential performance hit.
 +         * On Intel Haswell 4x8 is always faster.
 +         */
 +        *kernel_type = nbnxnk4xN_SIMD_4xN;
 +
 +#ifndef GMX_SIMD_HAVE_FMA
 +        if (EEL_PME_EWALD(ir->coulombtype) ||
 +            EVDW_PME(ir->vdwtype))
 +        {
 +            /* We have Ewald kernels without FMA (Intel Sandy/Ivy Bridge).
 +             * There are enough instructions to make 2x(4+4) efficient.
 +             */
 +            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 +        }
 +#endif
 +#endif  /* GMX_NBNXN_SIMD_2XNN && GMX_NBNXN_SIMD_4XN */
 +
 +
 +        if (getenv("GMX_NBNXN_SIMD_4XN") != NULL)
 +        {
 +#ifdef GMX_NBNXN_SIMD_4XN
 +            *kernel_type = nbnxnk4xN_SIMD_4xN;
 +#else
 +            gmx_fatal(FARGS, "SIMD 4xN kernels requested, but Gromacs has been compiled without support for these kernels");
 +#endif
 +        }
 +        if (getenv("GMX_NBNXN_SIMD_2XNN") != NULL)
 +        {
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 +#else
 +            gmx_fatal(FARGS, "SIMD 2x(N+N) kernels requested, but Gromacs has been compiled without support for these kernels");
 +#endif
 +        }
 +
 +        /* Analytical Ewald exclusion correction is only an option in
 +         * the SIMD kernel.
 +         * Since table lookup's don't parallelize with SIMD, analytical
 +         * will probably always be faster for a SIMD width of 8 or more.
 +         * With FMA analytical is sometimes faster for a width if 4 as well.
 +         * On BlueGene/Q, this is faster regardless of precision.
 +         * In single precision, this is faster on Bulldozer.
 +         */
 +#if GMX_SIMD_REAL_WIDTH >= 8 || \
 +        (GMX_SIMD_REAL_WIDTH >= 4 && defined GMX_SIMD_HAVE_FMA && !defined GMX_DOUBLE) || \
 +        defined GMX_SIMD_IBM_QPX
 +        *ewald_excl = ewaldexclAnalytical;
 +#endif
 +        if (getenv("GMX_NBNXN_EWALD_TABLE") != NULL)
 +        {
 +            *ewald_excl = ewaldexclTable;
 +        }
 +        if (getenv("GMX_NBNXN_EWALD_ANALYTICAL") != NULL)
 +        {
 +            *ewald_excl = ewaldexclAnalytical;
 +        }
 +
 +    }
 +#endif /* GMX_NBNXN_SIMD */
 +}
 +
 +
 +const char *lookup_nbnxn_kernel_name(int kernel_type)
 +{
 +    const char *returnvalue = NULL;
 +    switch (kernel_type)
 +    {
 +        case nbnxnkNotSet:
 +            returnvalue = "not set";
 +            break;
 +        case nbnxnk4x4_PlainC:
 +            returnvalue = "plain C";
 +            break;
 +        case nbnxnk4xN_SIMD_4xN:
 +        case nbnxnk4xN_SIMD_2xNN:
 +#ifdef GMX_NBNXN_SIMD
 +#if defined GMX_SIMD_X86_SSE2
 +            returnvalue = "SSE2";
 +#elif defined GMX_SIMD_X86_SSE4_1
 +            returnvalue = "SSE4.1";
 +#elif defined GMX_SIMD_X86_AVX_128_FMA
 +            returnvalue = "AVX_128_FMA";
 +#elif defined GMX_SIMD_X86_AVX_256
 +            returnvalue = "AVX_256";
 +#elif defined GMX_SIMD_X86_AVX2_256
 +            returnvalue = "AVX2_256";
 +#else
 +            returnvalue = "SIMD";
 +#endif
 +#else  /* GMX_NBNXN_SIMD */
 +            returnvalue = "not available";
 +#endif /* GMX_NBNXN_SIMD */
 +            break;
 +        case nbnxnk8x8x8_CUDA: returnvalue   = "CUDA"; break;
 +        case nbnxnk8x8x8_PlainC: returnvalue = "plain C"; break;
 +
 +        case nbnxnkNR:
 +        default:
 +            gmx_fatal(FARGS, "Illegal kernel type selected");
 +            returnvalue = NULL;
 +            break;
 +    }
 +    return returnvalue;
 +};
 +
 +static void pick_nbnxn_kernel(FILE                *fp,
 +                              const t_commrec     *cr,
 +                              gmx_bool             use_simd_kernels,
 +                              gmx_bool             bUseGPU,
 +                              gmx_bool             bEmulateGPU,
 +                              const t_inputrec    *ir,
 +                              int                 *kernel_type,
 +                              int                 *ewald_excl,
 +                              gmx_bool             bDoNonbonded)
 +{
 +    assert(kernel_type);
 +
 +    *kernel_type = nbnxnkNotSet;
 +    *ewald_excl  = ewaldexclTable;
 +
 +    if (bEmulateGPU)
 +    {
 +        *kernel_type = nbnxnk8x8x8_PlainC;
 +
 +        if (bDoNonbonded)
 +        {
 +            md_print_warn(cr, fp, "Emulating a GPU run on the CPU (slow)");
 +        }
 +    }
 +    else if (bUseGPU)
 +    {
 +        *kernel_type = nbnxnk8x8x8_CUDA;
 +    }
 +
 +    if (*kernel_type == nbnxnkNotSet)
 +    {
 +        /* LJ PME with LB combination rule does 7 mesh operations.
 +         * This so slow that we don't compile SIMD non-bonded kernels for that.
 +         */
 +        if (use_simd_kernels &&
 +            nbnxn_acceleration_supported(fp, cr, ir, FALSE))
 +        {
 +            pick_nbnxn_kernel_cpu(ir, kernel_type, ewald_excl);
 +        }
 +        else
 +        {
 +            *kernel_type = nbnxnk4x4_PlainC;
 +        }
 +    }
 +
 +    if (bDoNonbonded && fp != NULL)
 +    {
 +        fprintf(fp, "\nUsing %s %dx%d non-bonded kernels\n\n",
 +                lookup_nbnxn_kernel_name(*kernel_type),
 +                nbnxn_kernel_pairlist_simple(*kernel_type) ? NBNXN_CPU_CLUSTER_I_SIZE : NBNXN_GPU_CLUSTER_SIZE,
 +                nbnxn_kernel_to_cj_size(*kernel_type));
 +    }
 +}
 +
 +static void pick_nbnxn_resources(const t_commrec     *cr,
 +                                 const gmx_hw_info_t *hwinfo,
 +                                 gmx_bool             bDoNonbonded,
 +                                 gmx_bool            *bUseGPU,
 +                                 gmx_bool            *bEmulateGPU,
 +                                 const gmx_gpu_opt_t *gpu_opt)
 +{
 +    gmx_bool bEmulateGPUEnvVarSet;
 +    char     gpu_err_str[STRLEN];
 +
 +    *bUseGPU = FALSE;
 +
 +    bEmulateGPUEnvVarSet = (getenv("GMX_EMULATE_GPU") != NULL);
 +
 +    /* Run GPU emulation mode if GMX_EMULATE_GPU is defined. Because
 +     * GPUs (currently) only handle non-bonded calculations, we will
 +     * automatically switch to emulation if non-bonded calculations are
 +     * turned off via GMX_NO_NONBONDED - this is the simple and elegant
 +     * way to turn off GPU initialization, data movement, and cleanup.
 +     *
 +     * GPU emulation can be useful to assess the performance one can expect by
 +     * adding GPU(s) to the machine. The conditional below allows this even
 +     * if mdrun is compiled without GPU acceleration support.
 +     * Note that you should freezing the system as otherwise it will explode.
 +     */
 +    *bEmulateGPU = (bEmulateGPUEnvVarSet ||
 +                    (!bDoNonbonded &&
 +                     gpu_opt->ncuda_dev_use > 0));
 +
 +    /* Enable GPU mode when GPUs are available or no GPU emulation is requested.
 +     */
 +    if (gpu_opt->ncuda_dev_use > 0 && !(*bEmulateGPU))
 +    {
 +        /* Each PP node will use the intra-node id-th device from the
 +         * list of detected/selected GPUs. */
 +        if (!init_gpu(cr->rank_pp_intranode, gpu_err_str,
 +                      &hwinfo->gpu_info, gpu_opt))
 +        {
 +            /* At this point the init should never fail as we made sure that
 +             * we have all the GPUs we need. If it still does, we'll bail. */
 +            gmx_fatal(FARGS, "On node %d failed to initialize GPU #%d: %s",
 +                      cr->nodeid,
 +                      get_gpu_device_id(&hwinfo->gpu_info, gpu_opt,
 +                                        cr->rank_pp_intranode),
 +                      gpu_err_str);
 +        }
 +
 +        /* Here we actually turn on hardware GPU acceleration */
 +        *bUseGPU = TRUE;
 +    }
 +}
 +
 +gmx_bool uses_simple_tables(int                 cutoff_scheme,
 +                            nonbonded_verlet_t *nbv,
 +                            int                 group)
 +{
 +    gmx_bool bUsesSimpleTables = TRUE;
 +    int      grp_index;
 +
 +    switch (cutoff_scheme)
 +    {
 +        case ecutsGROUP:
 +            bUsesSimpleTables = TRUE;
 +            break;
 +        case ecutsVERLET:
 +            assert(NULL != nbv && NULL != nbv->grp);
 +            grp_index         = (group < 0) ? 0 : (nbv->ngrp - 1);
 +            bUsesSimpleTables = nbnxn_kernel_pairlist_simple(nbv->grp[grp_index].kernel_type);
 +            break;
 +        default:
 +            gmx_incons("unimplemented");
 +    }
 +    return bUsesSimpleTables;
 +}
 +
 +static void init_ewald_f_table(interaction_const_t *ic,
 +                               gmx_bool             bUsesSimpleTables,
 +                               real                 rtab)
 +{
 +    real maxr;
 +
 +    if (bUsesSimpleTables)
 +    {
 +        /* With a spacing of 0.0005 we are at the force summation accuracy
 +         * for the SSE kernels for "normal" atomistic simulations.
 +         */
 +        ic->tabq_scale = ewald_spline3_table_scale(ic->ewaldcoeff_q,
 +                                                   ic->rcoulomb);
 +
 +        maxr           = (rtab > ic->rcoulomb) ? rtab : ic->rcoulomb;
 +        ic->tabq_size  = (int)(maxr*ic->tabq_scale) + 2;
 +    }
 +    else
 +    {
 +        ic->tabq_size = GPU_EWALD_COULOMB_FORCE_TABLE_SIZE;
 +        /* Subtract 2 iso 1 to avoid access out of range due to rounding */
 +        ic->tabq_scale = (ic->tabq_size - 2)/ic->rcoulomb;
 +    }
 +
 +    sfree_aligned(ic->tabq_coul_FDV0);
 +    sfree_aligned(ic->tabq_coul_F);
 +    sfree_aligned(ic->tabq_coul_V);
 +
 +    sfree_aligned(ic->tabq_vdw_FDV0);
 +    sfree_aligned(ic->tabq_vdw_F);
 +    sfree_aligned(ic->tabq_vdw_V);
 +
 +    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype))
 +    {
 +        /* Create the original table data in FDV0 */
 +        snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32);
 +        snew_aligned(ic->tabq_coul_F, ic->tabq_size, 32);
 +        snew_aligned(ic->tabq_coul_V, ic->tabq_size, 32);
 +        table_spline3_fill_ewald_lr(ic->tabq_coul_F, ic->tabq_coul_V, ic->tabq_coul_FDV0,
 +                                    ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_q, v_q_ewald_lr);
 +    }
 +
 +    if (EVDW_PME(ic->vdwtype))
 +    {
 +        snew_aligned(ic->tabq_vdw_FDV0, ic->tabq_size*4, 32);
 +        snew_aligned(ic->tabq_vdw_F, ic->tabq_size, 32);
 +        snew_aligned(ic->tabq_vdw_V, ic->tabq_size, 32);
 +        table_spline3_fill_ewald_lr(ic->tabq_vdw_F, ic->tabq_vdw_V, ic->tabq_vdw_FDV0,
 +                                    ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_lj, v_lj_ewald_lr);
 +    }
 +}
 +
 +void init_interaction_const_tables(FILE                *fp,
 +                                   interaction_const_t *ic,
 +                                   gmx_bool             bUsesSimpleTables,
 +                                   real                 rtab)
 +{
 +    real spacing;
 +
 +    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype) || EVDW_PME(ic->vdwtype))
 +    {
 +        init_ewald_f_table(ic, bUsesSimpleTables, rtab);
 +
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "Initialized non-bonded Ewald correction tables, spacing: %.2e size: %d\n\n",
 +                    1/ic->tabq_scale, ic->tabq_size);
 +        }
 +    }
 +}
 +
 +static void clear_force_switch_constants(shift_consts_t *sc)
 +{
 +    sc->c2   = 0;
 +    sc->c3   = 0;
 +    sc->cpot = 0;
 +}
 +
 +static void force_switch_constants(real p,
 +                                   real rsw, real rc,
 +                                   shift_consts_t *sc)
 +{
 +    /* Here we determine the coefficient for shifting the force to zero
 +     * between distance rsw and the cut-off rc.
 +     * For a potential of r^-p, we have force p*r^-(p+1).
 +     * But to save flops we absorb p in the coefficient.
 +     * Thus we get:
 +     * force/p   = r^-(p+1) + c2*r^2 + c3*r^3
 +     * potential = r^-p + c2/3*r^3 + c3/4*r^4 + cpot
 +     */
 +    sc->c2   =  ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 2));
 +    sc->c3   = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 3));
 +    sc->cpot = -pow(rc, -p) + p*sc->c2/3*pow(rc - rsw, 3) + p*sc->c3/4*pow(rc - rsw, 4);
 +}
 +
 +static void potential_switch_constants(real rsw, real rc,
 +                                       switch_consts_t *sc)
 +{
 +    /* The switch function is 1 at rsw and 0 at rc.
 +     * The derivative and second derivate are zero at both ends.
 +     * rsw        = max(r - r_switch, 0)
 +     * sw         = 1 + c3*rsw^3 + c4*rsw^4 + c5*rsw^5
 +     * dsw        = 3*c3*rsw^2 + 4*c4*rsw^3 + 5*c5*rsw^4
 +     * force      = force*dsw - potential*sw
 +     * potential *= sw
 +     */
 +    sc->c3 = -10*pow(rc - rsw, -3);
 +    sc->c4 =  15*pow(rc - rsw, -4);
 +    sc->c5 =  -6*pow(rc - rsw, -5);
 +}
 +
 +static void
 +init_interaction_const(FILE                       *fp,
 +                       const t_commrec gmx_unused *cr,
 +                       interaction_const_t       **interaction_const,
 +                       const t_forcerec           *fr,
 +                       real                        rtab)
 +{
 +    interaction_const_t *ic;
 +    gmx_bool             bUsesSimpleTables = TRUE;
 +
 +    snew(ic, 1);
 +
 +    /* Just allocate something so we can free it */
 +    snew_aligned(ic->tabq_coul_FDV0, 16, 32);
 +    snew_aligned(ic->tabq_coul_F, 16, 32);
 +    snew_aligned(ic->tabq_coul_V, 16, 32);
 +
 +    ic->rlist           = fr->rlist;
 +    ic->rlistlong       = fr->rlistlong;
 +
 +    /* Lennard-Jones */
 +    ic->vdwtype         = fr->vdwtype;
 +    ic->vdw_modifier    = fr->vdw_modifier;
 +    ic->rvdw            = fr->rvdw;
 +    ic->rvdw_switch     = fr->rvdw_switch;
 +    ic->ewaldcoeff_lj   = fr->ewaldcoeff_lj;
 +    ic->ljpme_comb_rule = fr->ljpme_combination_rule;
 +    ic->sh_lj_ewald     = 0;
 +    clear_force_switch_constants(&ic->dispersion_shift);
 +    clear_force_switch_constants(&ic->repulsion_shift);
 +
 +    switch (ic->vdw_modifier)
 +    {
 +        case eintmodPOTSHIFT:
 +            /* Only shift the potential, don't touch the force */
 +            ic->dispersion_shift.cpot = -pow(ic->rvdw, -6.0);
 +            ic->repulsion_shift.cpot  = -pow(ic->rvdw, -12.0);
 +            if (EVDW_PME(ic->vdwtype))
 +            {
 +                real crc2;
 +
 +                crc2            = sqr(ic->ewaldcoeff_lj*ic->rvdw);
 +                ic->sh_lj_ewald = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*pow(ic->rvdw, -6.0);
 +            }
 +            break;
 +        case eintmodFORCESWITCH:
 +            /* Switch the force, switch and shift the potential */
 +            force_switch_constants(6.0, ic->rvdw_switch, ic->rvdw,
 +                                   &ic->dispersion_shift);
 +            force_switch_constants(12.0, ic->rvdw_switch, ic->rvdw,
 +                                   &ic->repulsion_shift);
 +            break;
 +        case eintmodPOTSWITCH:
 +            /* Switch the potential and force */
 +            potential_switch_constants(ic->rvdw_switch, ic->rvdw,
 +                                       &ic->vdw_switch);
 +            break;
 +        case eintmodNONE:
 +        case eintmodEXACTCUTOFF:
 +            /* Nothing to do here */
 +            break;
 +        default:
 +            gmx_incons("unimplemented potential modifier");
 +    }
 +
 +    ic->sh_invrc6 = -ic->dispersion_shift.cpot;
 +
 +    /* Electrostatics */
 +    ic->eeltype          = fr->eeltype;
 +    ic->coulomb_modifier = fr->coulomb_modifier;
 +    ic->rcoulomb         = fr->rcoulomb;
 +    ic->epsilon_r        = fr->epsilon_r;
 +    ic->epsfac           = fr->epsfac;
 +    ic->ewaldcoeff_q     = fr->ewaldcoeff_q;
 +
 +    if (fr->coulomb_modifier == eintmodPOTSHIFT)
 +    {
 +        ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb);
 +    }
 +    else
 +    {
 +        ic->sh_ewald = 0;
 +    }
 +
 +    /* Reaction-field */
 +    if (EEL_RF(ic->eeltype))
 +    {
 +        ic->epsilon_rf = fr->epsilon_rf;
 +        ic->k_rf       = fr->k_rf;
 +        ic->c_rf       = fr->c_rf;
 +    }
 +    else
 +    {
 +        /* For plain cut-off we might use the reaction-field kernels */
 +        ic->epsilon_rf = ic->epsilon_r;
 +        ic->k_rf       = 0;
 +        if (fr->coulomb_modifier == eintmodPOTSHIFT)
 +        {
 +            ic->c_rf   = 1/ic->rcoulomb;
 +        }
 +        else
 +        {
 +            ic->c_rf   = 0;
 +        }
 +    }
 +
 +    if (fp != NULL)
 +    {
 +        real dispersion_shift;
 +
 +        dispersion_shift = ic->dispersion_shift.cpot;
 +        if (EVDW_PME(ic->vdwtype))
 +        {
 +            dispersion_shift -= ic->sh_lj_ewald;
 +        }
 +        fprintf(fp, "Potential shift: LJ r^-12: %.3e r^-6: %.3e",
 +                ic->repulsion_shift.cpot, dispersion_shift);
 +
 +        if (ic->eeltype == eelCUT)
 +        {
 +            fprintf(fp, ", Coulomb %.e", -ic->c_rf);
 +        }
 +        else if (EEL_PME(ic->eeltype))
 +        {
 +            fprintf(fp, ", Ewald %.3e", -ic->sh_ewald);
 +        }
 +        fprintf(fp, "\n");
 +    }
 +
 +    *interaction_const = ic;
 +
 +    if (fr->nbv != NULL && fr->nbv->bUseGPU)
 +    {
 +        nbnxn_cuda_init_const(fr->nbv->cu_nbv, ic, fr->nbv->grp);
 +
 +        /* With tMPI + GPUs some ranks may be sharing GPU(s) and therefore
 +         * also sharing texture references. To keep the code simple, we don't
 +         * treat texture references as shared resources, but this means that
 +         * the coulomb_tab and nbfp texture refs will get updated by multiple threads.
 +         * Hence, to ensure that the non-bonded kernels don't start before all
 +         * texture binding operations are finished, we need to wait for all ranks
 +         * to arrive here before continuing.
 +         *
 +         * Note that we could omit this barrier if GPUs are not shared (or
 +         * texture objects are used), but as this is initialization code, there
 +         * is not point in complicating things.
 +         */
 +#ifdef GMX_THREAD_MPI
 +        if (PAR(cr))
 +        {
 +            gmx_barrier(cr);
 +        }
 +#endif  /* GMX_THREAD_MPI */
 +    }
 +
 +    bUsesSimpleTables = uses_simple_tables(fr->cutoff_scheme, fr->nbv, -1);
 +    init_interaction_const_tables(fp, ic, bUsesSimpleTables, rtab);
 +}
 +
 +static void init_nb_verlet(FILE                *fp,
 +                           nonbonded_verlet_t **nb_verlet,
 +                           gmx_bool             bFEP_NonBonded,
 +                           const t_inputrec    *ir,
 +                           const t_forcerec    *fr,
 +                           const t_commrec     *cr,
 +                           const char          *nbpu_opt)
 +{
 +    nonbonded_verlet_t *nbv;
 +    int                 i;
 +    char               *env;
 +    gmx_bool            bEmulateGPU, bHybridGPURun = FALSE;
 +
 +    nbnxn_alloc_t      *nb_alloc;
 +    nbnxn_free_t       *nb_free;
 +
 +    snew(nbv, 1);
 +
 +    pick_nbnxn_resources(cr, fr->hwinfo,
 +                         fr->bNonbonded,
 +                         &nbv->bUseGPU,
 +                         &bEmulateGPU,
 +                         fr->gpu_opt);
 +
 +    nbv->nbs = NULL;
 +
 +    nbv->ngrp = (DOMAINDECOMP(cr) ? 2 : 1);
 +    for (i = 0; i < nbv->ngrp; i++)
 +    {
 +        nbv->grp[i].nbl_lists.nnbl = 0;
 +        nbv->grp[i].nbat           = NULL;
 +        nbv->grp[i].kernel_type    = nbnxnkNotSet;
 +
 +        if (i == 0) /* local */
 +        {
 +            pick_nbnxn_kernel(fp, cr, fr->use_simd_kernels,
 +                              nbv->bUseGPU, bEmulateGPU, ir,
 +                              &nbv->grp[i].kernel_type,
 +                              &nbv->grp[i].ewald_excl,
 +                              fr->bNonbonded);
 +        }
 +        else /* non-local */
 +        {
 +            if (nbpu_opt != NULL && strcmp(nbpu_opt, "gpu_cpu") == 0)
 +            {
 +                /* Use GPU for local, select a CPU kernel for non-local */
 +                pick_nbnxn_kernel(fp, cr, fr->use_simd_kernels,
 +                                  FALSE, FALSE, ir,
 +                                  &nbv->grp[i].kernel_type,
 +                                  &nbv->grp[i].ewald_excl,
 +                                  fr->bNonbonded);
 +
 +                bHybridGPURun = TRUE;
 +            }
 +            else
 +            {
 +                /* Use the same kernel for local and non-local interactions */
 +                nbv->grp[i].kernel_type = nbv->grp[0].kernel_type;
 +                nbv->grp[i].ewald_excl  = nbv->grp[0].ewald_excl;
 +            }
 +        }
 +    }
 +
 +    if (nbv->bUseGPU)
 +    {
 +        /* init the NxN GPU data; the last argument tells whether we'll have
 +         * both local and non-local NB calculation on GPU */
 +        nbnxn_cuda_init(fp, &nbv->cu_nbv,
 +                        &fr->hwinfo->gpu_info, fr->gpu_opt,
 +                        cr->rank_pp_intranode,
 +                        (nbv->ngrp > 1) && !bHybridGPURun);
 +
 +        if ((env = getenv("GMX_NB_MIN_CI")) != NULL)
 +        {
 +            char *end;
 +
 +            nbv->min_ci_balanced = strtol(env, &end, 10);
 +            if (!end || (*end != 0) || nbv->min_ci_balanced <= 0)
 +            {
 +                gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, positive integer required", env);
 +            }
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "Neighbor-list balancing parameter: %d (passed as env. var.)\n",
 +                        nbv->min_ci_balanced);
 +            }
 +        }
 +        else
 +        {
 +            nbv->min_ci_balanced = nbnxn_cuda_min_ci_balanced(nbv->cu_nbv);
 +            if (debug)
 +            {
 +                fprintf(debug, "Neighbor-list balancing parameter: %d (auto-adjusted to the number of GPU multi-processors)\n",
 +                        nbv->min_ci_balanced);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        nbv->min_ci_balanced = 0;
 +    }
 +
 +    *nb_verlet = nbv;
 +
 +    nbnxn_init_search(&nbv->nbs,
 +                      DOMAINDECOMP(cr) ? &cr->dd->nc : NULL,
 +                      DOMAINDECOMP(cr) ? domdec_zones(cr->dd) : NULL,
 +                      bFEP_NonBonded,
 +                      gmx_omp_nthreads_get(emntNonbonded));
 +
 +    for (i = 0; i < nbv->ngrp; i++)
 +    {
 +        if (nbv->grp[0].kernel_type == nbnxnk8x8x8_CUDA)
 +        {
 +            nb_alloc = &pmalloc;
 +            nb_free  = &pfree;
 +        }
 +        else
 +        {
 +            nb_alloc = NULL;
 +            nb_free  = NULL;
 +        }
 +
 +        nbnxn_init_pairlist_set(&nbv->grp[i].nbl_lists,
 +                                nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type),
 +                                /* 8x8x8 "non-simple" lists are ATM always combined */
 +                                !nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type),
 +                                nb_alloc, nb_free);
 +
 +        if (i == 0 ||
 +            nbv->grp[0].kernel_type != nbv->grp[i].kernel_type)
 +        {
 +            gmx_bool bSimpleList;
 +            int      enbnxninitcombrule;
 +
 +            bSimpleList = nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type);
 +
 +            if (bSimpleList && (fr->vdwtype == evdwCUT && (fr->vdw_modifier == eintmodNONE || fr->vdw_modifier == eintmodPOTSHIFT)))
 +            {
 +                /* Plain LJ cut-off: we can optimize with combination rules */
 +                enbnxninitcombrule = enbnxninitcombruleDETECT;
 +            }
 +            else if (fr->vdwtype == evdwPME)
 +            {
 +                /* LJ-PME: we need to use a combination rule for the grid */
 +                if (fr->ljpme_combination_rule == eljpmeGEOM)
 +                {
 +                    enbnxninitcombrule = enbnxninitcombruleGEOM;
 +                }
 +                else
 +                {
 +                    enbnxninitcombrule = enbnxninitcombruleLB;
 +                }
 +            }
 +            else
 +            {
 +                /* We use a full combination matrix: no rule required */
 +                enbnxninitcombrule = enbnxninitcombruleNONE;
 +            }
 +
 +
 +            snew(nbv->grp[i].nbat, 1);
 +            nbnxn_atomdata_init(fp,
 +                                nbv->grp[i].nbat,
 +                                nbv->grp[i].kernel_type,
 +                                enbnxninitcombrule,
 +                                fr->ntype, fr->nbfp,
 +                                ir->opts.ngener,
 +                                bSimpleList ? gmx_omp_nthreads_get(emntNonbonded) : 1,
 +                                nb_alloc, nb_free);
 +        }
 +        else
 +        {
 +            nbv->grp[i].nbat = nbv->grp[0].nbat;
 +        }
 +    }
 +}
 +
 +void init_forcerec(FILE              *fp,
 +                   const output_env_t oenv,
 +                   t_forcerec        *fr,
 +                   t_fcdata          *fcd,
 +                   const t_inputrec  *ir,
 +                   const gmx_mtop_t  *mtop,
 +                   const t_commrec   *cr,
 +                   matrix             box,
 +                   const char        *tabfn,
 +                   const char        *tabafn,
 +                   const char        *tabpfn,
 +                   const char        *tabbfn,
 +                   const char        *nbpu_opt,
 +                   gmx_bool           bNoSolvOpt,
 +                   real               print_force)
 +{
 +    int            i, j, m, natoms, ngrp, negp_pp, negptable, egi, egj;
 +    real           rtab;
 +    char          *env;
 +    double         dbl;
 +    const t_block *cgs;
 +    gmx_bool       bGenericKernelOnly;
 +    gmx_bool       bMakeTables, bMakeSeparate14Table, bSomeNormalNbListsAreInUse;
 +    gmx_bool       bFEP_NonBonded;
 +    t_nblists     *nbl;
 +    int           *nm_ind, egp_flags;
 +
 +    if (fr->hwinfo == NULL)
 +    {
 +        /* Detect hardware, gather information.
 +         * In mdrun, hwinfo has already been set before calling init_forcerec.
 +         * Here we ignore GPUs, as tools will not use them anyhow.
 +         */
 +        fr->hwinfo = gmx_detect_hardware(fp, cr, FALSE);
 +    }
 +
 +    /* By default we turn SIMD kernels on, but it might be turned off further down... */
 +    fr->use_simd_kernels = TRUE;
 +
 +    fr->bDomDec = DOMAINDECOMP(cr);
 +
 +    natoms = mtop->natoms;
 +
 +    if (check_box(ir->ePBC, box))
 +    {
 +        gmx_fatal(FARGS, check_box(ir->ePBC, box));
 +    }
 +
 +    /* Test particle insertion ? */
 +    if (EI_TPI(ir->eI))
 +    {
 +        /* Set to the size of the molecule to be inserted (the last one) */
 +        /* Because of old style topologies, we have to use the last cg
 +         * instead of the last molecule type.
 +         */
 +        cgs       = &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].cgs;
 +        fr->n_tpi = cgs->index[cgs->nr] - cgs->index[cgs->nr-1];
 +        if (fr->n_tpi != mtop->mols.index[mtop->mols.nr] - mtop->mols.index[mtop->mols.nr-1])
 +        {
 +            gmx_fatal(FARGS, "The molecule to insert can not consist of multiple charge groups.\nMake it a single charge group.");
 +        }
 +    }
 +    else
 +    {
 +        fr->n_tpi = 0;
 +    }
 +
 +    /* Copy AdResS parameters */
 +    if (ir->bAdress)
 +    {
 +        fr->adress_type           = ir->adress->type;
 +        fr->adress_const_wf       = ir->adress->const_wf;
 +        fr->adress_ex_width       = ir->adress->ex_width;
 +        fr->adress_hy_width       = ir->adress->hy_width;
 +        fr->adress_icor           = ir->adress->icor;
 +        fr->adress_site           = ir->adress->site;
 +        fr->adress_ex_forcecap    = ir->adress->ex_forcecap;
 +        fr->adress_do_hybridpairs = ir->adress->do_hybridpairs;
 +
 +
 +        snew(fr->adress_group_explicit, ir->adress->n_energy_grps);
 +        for (i = 0; i < ir->adress->n_energy_grps; i++)
 +        {
 +            fr->adress_group_explicit[i] = ir->adress->group_explicit[i];
 +        }
 +
 +        fr->n_adress_tf_grps = ir->adress->n_tf_grps;
 +        snew(fr->adress_tf_table_index, fr->n_adress_tf_grps);
 +        for (i = 0; i < fr->n_adress_tf_grps; i++)
 +        {
 +            fr->adress_tf_table_index[i] = ir->adress->tf_table_index[i];
 +        }
 +        copy_rvec(ir->adress->refs, fr->adress_refs);
 +    }
 +    else
 +    {
 +        fr->adress_type           = eAdressOff;
 +        fr->adress_do_hybridpairs = FALSE;
 +    }
 +
 +    /* Copy the user determined parameters */
 +    fr->userint1  = ir->userint1;
 +    fr->userint2  = ir->userint2;
 +    fr->userint3  = ir->userint3;
 +    fr->userint4  = ir->userint4;
 +    fr->userreal1 = ir->userreal1;
 +    fr->userreal2 = ir->userreal2;
 +    fr->userreal3 = ir->userreal3;
 +    fr->userreal4 = ir->userreal4;
 +
 +    /* Shell stuff */
 +    fr->fc_stepsize = ir->fc_stepsize;
 +
 +    /* Free energy */
 +    fr->efep        = ir->efep;
 +    fr->sc_alphavdw = ir->fepvals->sc_alpha;
 +    if (ir->fepvals->bScCoul)
 +    {
 +        fr->sc_alphacoul  = ir->fepvals->sc_alpha;
 +        fr->sc_sigma6_min = pow(ir->fepvals->sc_sigma_min, 6);
 +    }
 +    else
 +    {
 +        fr->sc_alphacoul  = 0;
 +        fr->sc_sigma6_min = 0; /* only needed when bScCoul is on */
 +    }
 +    fr->sc_power      = ir->fepvals->sc_power;
 +    fr->sc_r_power    = ir->fepvals->sc_r_power;
 +    fr->sc_sigma6_def = pow(ir->fepvals->sc_sigma, 6);
 +
 +    env = getenv("GMX_SCSIGMA_MIN");
 +    if (env != NULL)
 +    {
 +        dbl = 0;
 +        sscanf(env, "%lf", &dbl);
 +        fr->sc_sigma6_min = pow(dbl, 6);
 +        if (fp)
 +        {
 +            fprintf(fp, "Setting the minimum soft core sigma to %g nm\n", dbl);
 +        }
 +    }
 +
 +    fr->bNonbonded = TRUE;
 +    if (getenv("GMX_NO_NONBONDED") != NULL)
 +    {
 +        /* turn off non-bonded calculations */
 +        fr->bNonbonded = FALSE;
 +        md_print_warn(cr, fp,
 +                      "Found environment variable GMX_NO_NONBONDED.\n"
 +                      "Disabling nonbonded calculations.\n");
 +    }
 +
 +    bGenericKernelOnly = FALSE;
 +
 +    /* We now check in the NS code whether a particular combination of interactions
 +     * can be used with water optimization, and disable it if that is not the case.
 +     */
 +
 +    if (getenv("GMX_NB_GENERIC") != NULL)
 +    {
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "Found environment variable GMX_NB_GENERIC.\n"
 +                    "Disabling all interaction-specific nonbonded kernels, will only\n"
 +                    "use the slow generic ones in src/gmxlib/nonbonded/nb_generic.c\n\n");
 +        }
 +        bGenericKernelOnly = TRUE;
 +    }
 +
 +    if (bGenericKernelOnly == TRUE)
 +    {
 +        bNoSolvOpt         = TRUE;
 +    }
 +
 +    if ( (getenv("GMX_DISABLE_SIMD_KERNELS") != NULL) || (getenv("GMX_NOOPTIMIZEDKERNELS") != NULL) )
 +    {
 +        fr->use_simd_kernels = FALSE;
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "\nFound environment variable GMX_DISABLE_SIMD_KERNELS.\n"
 +                    "Disabling the usage of any SIMD-specific kernel routines (e.g. SSE2/SSE4.1/AVX).\n\n");
 +        }
 +    }
 +
 +    fr->bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
 +
 +    /* Check if we can/should do all-vs-all kernels */
 +    fr->bAllvsAll       = can_use_allvsall(ir, FALSE, NULL, NULL);
 +    fr->AllvsAll_work   = NULL;
 +    fr->AllvsAll_workgb = NULL;
 +
 +    /* All-vs-all kernels have not been implemented in 4.6, and
 +     * the SIMD group kernels are also buggy in this case. Non-SIMD
 +     * group kernels are OK. See Redmine #1249. */
 +    if (fr->bAllvsAll)
 +    {
 +        fr->bAllvsAll            = FALSE;
 +        fr->use_simd_kernels     = FALSE;
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "\nYour simulation settings would have triggered the efficient all-vs-all\n"
 +                    "kernels in GROMACS 4.5, but these have not been implemented in GROMACS\n"
 +                    "4.6. Also, we can't use the accelerated SIMD kernels here because\n"
 +                    "of an unfixed bug. The reference C kernels are correct, though, so\n"
 +                    "we are proceeding by disabling all CPU architecture-specific\n"
 +                    "(e.g. SSE2/SSE4/AVX) routines. If performance is important, please\n"
 +                    "use GROMACS 4.5.7 or try cutoff-scheme = Verlet.\n\n");
 +        }
 +    }
 +
 +    /* Neighbour searching stuff */
 +    fr->cutoff_scheme = ir->cutoff_scheme;
 +    fr->bGrid         = (ir->ns_type == ensGRID);
 +    fr->ePBC          = ir->ePBC;
 +
 +    if (fr->cutoff_scheme == ecutsGROUP)
 +    {
 +        const char *note = "NOTE: This file uses the deprecated 'group' cutoff_scheme. This will be\n"
 +            "removed in a future release when 'verlet' supports all interaction forms.\n";
 +
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, "\n%s\n", note);
 +        }
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "\n%s\n", note);
 +        }
 +    }
 +
 +    /* Determine if we will do PBC for distances in bonded interactions */
 +    if (fr->ePBC == epbcNONE)
 +    {
 +        fr->bMolPBC = FALSE;
 +    }
 +    else
 +    {
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            /* The group cut-off scheme and SHAKE assume charge groups
 +             * are whole, but not using molpbc is faster in most cases.
 +             */
 +            if (fr->cutoff_scheme == ecutsGROUP ||
 +                (ir->eConstrAlg == econtSHAKE &&
 +                 (gmx_mtop_ftype_count(mtop, F_CONSTR) > 0 ||
 +                  gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0)))
 +            {
 +                fr->bMolPBC = ir->bPeriodicMols;
 +            }
 +            else
 +            {
 +                fr->bMolPBC = TRUE;
 +                if (getenv("GMX_USE_GRAPH") != NULL)
 +                {
 +                    fr->bMolPBC = FALSE;
 +                    if (fp)
 +                    {
 +                        fprintf(fp, "\nGMX_MOLPBC is set, using the graph for bonded interactions\n\n");
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            fr->bMolPBC = dd_bonded_molpbc(cr->dd, fr->ePBC);
 +        }
 +    }
 +    fr->bGB = (ir->implicit_solvent == eisGBSA);
 +
 +    fr->rc_scaling = ir->refcoord_scaling;
 +    copy_rvec(ir->posres_com, fr->posres_com);
 +    copy_rvec(ir->posres_comB, fr->posres_comB);
 +    fr->rlist                    = cutoff_inf(ir->rlist);
 +    fr->rlistlong                = cutoff_inf(ir->rlistlong);
 +    fr->eeltype                  = ir->coulombtype;
 +    fr->vdwtype                  = ir->vdwtype;
 +    fr->ljpme_combination_rule   = ir->ljpme_combination_rule;
 +
 +    fr->coulomb_modifier = ir->coulomb_modifier;
 +    fr->vdw_modifier     = ir->vdw_modifier;
 +
 +    /* Electrostatics: Translate from interaction-setting-in-mdp-file to kernel interaction format */
 +    switch (fr->eeltype)
 +    {
 +        case eelCUT:
 +            fr->nbkernel_elec_interaction = (fr->bGB) ? GMX_NBKERNEL_ELEC_GENERALIZEDBORN : GMX_NBKERNEL_ELEC_COULOMB;
 +            break;
 +
 +        case eelRF:
 +        case eelGRF:
 +        case eelRF_NEC:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
 +            break;
 +
 +        case eelRF_ZERO:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
 +            fr->coulomb_modifier          = eintmodEXACTCUTOFF;
 +            break;
 +
 +        case eelSWITCH:
 +        case eelSHIFT:
 +        case eelUSER:
 +        case eelENCADSHIFT:
 +        case eelPMESWITCH:
 +        case eelPMEUSER:
 +        case eelPMEUSERSWITCH:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE;
 +            break;
 +
 +        case eelPME:
 +        case eelEWALD:
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_EWALD;
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Unsupported electrostatic interaction: %s", eel_names[fr->eeltype]);
 +            break;
 +    }
 +
 +    /* Vdw: Translate from mdp settings to kernel format */
 +    switch (fr->vdwtype)
 +    {
 +        case evdwCUT:
 +            if (fr->bBHAM)
 +            {
 +                fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_BUCKINGHAM;
 +            }
 +            else
 +            {
 +                fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LENNARDJONES;
 +            }
 +            break;
 +        case evdwPME:
 +            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LJEWALD;
 +            break;
 +
 +        case evdwSWITCH:
 +        case evdwSHIFT:
 +        case evdwUSER:
 +        case evdwENCADSHIFT:
 +            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Unsupported vdw interaction: %s", evdw_names[fr->vdwtype]);
 +            break;
 +    }
 +
 +    /* These start out identical to ir, but might be altered if we e.g. tabulate the interaction in the kernel */
 +    fr->nbkernel_elec_modifier    = fr->coulomb_modifier;
 +    fr->nbkernel_vdw_modifier     = fr->vdw_modifier;
 +
 +    fr->bTwinRange = fr->rlistlong > fr->rlist;
 +    fr->bEwald     = (EEL_PME(fr->eeltype) || fr->eeltype == eelEWALD);
 +
 +    fr->reppow     = mtop->ffparams.reppow;
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        fr->bvdwtab    = ((fr->vdwtype != evdwCUT || !gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +                          && !EVDW_PME(fr->vdwtype));
 +        /* We have special kernels for standard Ewald and PME, but the pme-switch ones are tabulated above */
 +        fr->bcoultab   = !(fr->eeltype == eelCUT ||
 +                           fr->eeltype == eelEWALD ||
 +                           fr->eeltype == eelPME ||
 +                           fr->eeltype == eelRF ||
 +                           fr->eeltype == eelRF_ZERO);
 +
 +        /* If the user absolutely wants different switch/shift settings for coul/vdw, it is likely
 +         * going to be faster to tabulate the interaction than calling the generic kernel.
 +         */
 +        if (fr->nbkernel_elec_modifier == eintmodPOTSWITCH && fr->nbkernel_vdw_modifier == eintmodPOTSWITCH)
 +        {
 +            if ((fr->rcoulomb_switch != fr->rvdw_switch) || (fr->rcoulomb != fr->rvdw))
 +            {
 +                fr->bcoultab = TRUE;
 +            }
 +        }
 +        else if ((fr->nbkernel_elec_modifier == eintmodPOTSHIFT && fr->nbkernel_vdw_modifier == eintmodPOTSHIFT) ||
 +                 ((fr->nbkernel_elec_interaction == GMX_NBKERNEL_ELEC_REACTIONFIELD &&
 +                   fr->nbkernel_elec_modifier == eintmodEXACTCUTOFF &&
 +                   (fr->nbkernel_vdw_modifier == eintmodPOTSWITCH || fr->nbkernel_vdw_modifier == eintmodPOTSHIFT))))
 +        {
 +            if (fr->rcoulomb != fr->rvdw)
 +            {
 +                fr->bcoultab = TRUE;
 +            }
 +        }
 +
 +        if (getenv("GMX_REQUIRE_TABLES"))
 +        {
 +            fr->bvdwtab  = TRUE;
 +            fr->bcoultab = TRUE;
 +        }
 +
 +        if (fp)
 +        {
 +            fprintf(fp, "Table routines are used for coulomb: %s\n", bool_names[fr->bcoultab]);
 +            fprintf(fp, "Table routines are used for vdw:     %s\n", bool_names[fr->bvdwtab ]);
 +        }
 +
 +        if (fr->bvdwtab == TRUE)
 +        {
 +            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
 +            fr->nbkernel_vdw_modifier    = eintmodNONE;
 +        }
 +        if (fr->bcoultab == TRUE)
 +        {
 +            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE;
 +            fr->nbkernel_elec_modifier    = eintmodNONE;
 +        }
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        if (!gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        {
 +            gmx_fatal(FARGS, "Cut-off scheme %S only supports LJ repulsion power 12", ecutscheme_names[ir->cutoff_scheme]);
 +        }
 +        fr->bvdwtab  = FALSE;
 +        fr->bcoultab = FALSE;
 +    }
 +
 +    /* Tables are used for direct ewald sum */
 +    if (fr->bEwald)
 +    {
 +        if (EEL_PME(ir->coulombtype))
 +        {
 +            if (fp)
 +            {
 +                fprintf(fp, "Will do PME sum in reciprocal space for electrostatic interactions.\n");
 +            }
 +            if (ir->coulombtype == eelP3M_AD)
 +            {
 +                please_cite(fp, "Hockney1988");
 +                please_cite(fp, "Ballenegger2012");
 +            }
 +            else
 +            {
 +                please_cite(fp, "Essmann95a");
 +            }
 +
 +            if (ir->ewald_geometry == eewg3DC)
 +            {
 +                if (fp)
 +                {
 +                    fprintf(fp, "Using the Ewald3DC correction for systems with a slab geometry.\n");
 +                }
 +                please_cite(fp, "In-Chul99a");
 +            }
 +        }
 +        fr->ewaldcoeff_q = calc_ewaldcoeff_q(ir->rcoulomb, ir->ewald_rtol);
 +        init_ewald_tab(&(fr->ewald_table), ir, fp);
 +        if (fp)
 +        {
 +            fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n",
 +                    1/fr->ewaldcoeff_q);
 +        }
 +    }
 +
 +    if (EVDW_PME(ir->vdwtype))
 +    {
 +        if (fp)
 +        {
 +            fprintf(fp, "Will do PME sum in reciprocal space for LJ dispersion interactions.\n");
 +        }
 +        please_cite(fp, "Essmann95a");
 +        fr->ewaldcoeff_lj = calc_ewaldcoeff_lj(ir->rvdw, ir->ewald_rtol_lj);
 +        if (fp)
 +        {
 +            fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for LJ Ewald\n",
 +                    1/fr->ewaldcoeff_lj);
 +        }
 +    }
 +
 +    /* Electrostatics */
 +    fr->epsilon_r       = ir->epsilon_r;
 +    fr->epsilon_rf      = ir->epsilon_rf;
 +    fr->fudgeQQ         = mtop->ffparams.fudgeQQ;
 +    fr->rcoulomb_switch = ir->rcoulomb_switch;
 +    fr->rcoulomb        = cutoff_inf(ir->rcoulomb);
 +
 +    /* Parameters for generalized RF */
 +    fr->zsquare = 0.0;
 +    fr->temp    = 0.0;
 +
 +    if (fr->eeltype == eelGRF)
 +    {
 +        init_generalized_rf(fp, mtop, ir, fr);
 +    }
 +
 +    fr->bF_NoVirSum = (EEL_FULL(fr->eeltype) || EVDW_PME(fr->vdwtype) ||
 +                       gmx_mtop_ftype_count(mtop, F_POSRES) > 0 ||
 +                       gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0 ||
 +                       IR_ELEC_FIELD(*ir) ||
 +                       (fr->adress_icor != eAdressICOff)
 +                       );
 +
 +    if (fr->cutoff_scheme == ecutsGROUP &&
 +        ncg_mtop(mtop) > fr->cg_nalloc && !DOMAINDECOMP(cr))
 +    {
 +        /* Count the total number of charge groups */
 +        fr->cg_nalloc = ncg_mtop(mtop);
 +        srenew(fr->cg_cm, fr->cg_nalloc);
 +    }
 +    if (fr->shift_vec == NULL)
 +    {
 +        snew(fr->shift_vec, SHIFTS);
 +    }
 +
 +    if (fr->fshift == NULL)
 +    {
 +        snew(fr->fshift, SHIFTS);
 +    }
 +
 +    if (fr->nbfp == NULL)
 +    {
 +        fr->ntype = mtop->ffparams.atnr;
 +        fr->nbfp  = mk_nbfp(&mtop->ffparams, fr->bBHAM);
 +        if (EVDW_PME(fr->vdwtype))
 +        {
 +            fr->ljpme_c6grid  = make_ljpme_c6grid(&mtop->ffparams, fr);
 +        }
 +    }
 +
 +    /* Copy the energy group exclusions */
 +    fr->egp_flags = ir->opts.egp_flags;
 +
 +    /* Van der Waals stuff */
 +    fr->rvdw        = cutoff_inf(ir->rvdw);
 +    fr->rvdw_switch = ir->rvdw_switch;
 +    if ((fr->vdwtype != evdwCUT) && (fr->vdwtype != evdwUSER) && !fr->bBHAM)
 +    {
 +        if (fr->rvdw_switch >= fr->rvdw)
 +        {
 +            gmx_fatal(FARGS, "rvdw_switch (%f) must be < rvdw (%f)",
 +                      fr->rvdw_switch, fr->rvdw);
 +        }
 +        if (fp)
 +        {
 +            fprintf(fp, "Using %s Lennard-Jones, switch between %g and %g nm\n",
 +                    (fr->eeltype == eelSWITCH) ? "switched" : "shifted",
 +                    fr->rvdw_switch, fr->rvdw);
 +        }
 +    }
 +
 +    if (fr->bBHAM && EVDW_PME(fr->vdwtype))
 +    {
 +        gmx_fatal(FARGS, "LJ PME not supported with Buckingham");
 +    }
 +
 +    if (fr->bBHAM && (fr->vdwtype == evdwSHIFT || fr->vdwtype == evdwSWITCH))
 +    {
 +        gmx_fatal(FARGS, "Switch/shift interaction not supported with Buckingham");
++    }
++
++    if (fr->bBHAM && fr->cutoff_scheme == ecutsVERLET)
++    {
++        gmx_fatal(FARGS, "Verlet cutoff-scheme is not supported with Buckingham");
 +    }
 +
 +    if (fp)
 +    {
 +        fprintf(fp, "Cut-off's:   NS: %g   Coulomb: %g   %s: %g\n",
 +                fr->rlist, fr->rcoulomb, fr->bBHAM ? "BHAM" : "LJ", fr->rvdw);
 +    }
 +
 +    fr->eDispCorr = ir->eDispCorr;
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        set_avcsixtwelve(fp, fr, mtop);
 +    }
 +
 +    if (fr->bBHAM)
 +    {
 +        set_bham_b_max(fp, fr, mtop);
 +    }
 +
 +    fr->gb_epsilon_solvent = ir->gb_epsilon_solvent;
 +
 +    /* Copy the GBSA data (radius, volume and surftens for each
 +     * atomtype) from the topology atomtype section to forcerec.
 +     */
 +    snew(fr->atype_radius, fr->ntype);
 +    snew(fr->atype_vol, fr->ntype);
 +    snew(fr->atype_surftens, fr->ntype);
 +    snew(fr->atype_gb_radius, fr->ntype);
 +    snew(fr->atype_S_hct, fr->ntype);
 +
 +    if (mtop->atomtypes.nr > 0)
 +    {
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_radius[i] = mtop->atomtypes.radius[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_vol[i] = mtop->atomtypes.vol[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_surftens[i] = mtop->atomtypes.surftens[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_gb_radius[i] = mtop->atomtypes.gb_radius[i];
 +        }
 +        for (i = 0; i < fr->ntype; i++)
 +        {
 +            fr->atype_S_hct[i] = mtop->atomtypes.S_hct[i];
 +        }
 +    }
 +
 +    /* Generate the GB table if needed */
 +    if (fr->bGB)
 +    {
 +#ifdef GMX_DOUBLE
 +        fr->gbtabscale = 2000;
 +#else
 +        fr->gbtabscale = 500;
 +#endif
 +
 +        fr->gbtabr = 100;
 +        fr->gbtab  = make_gb_table(oenv, fr);
 +
 +        init_gb(&fr->born, fr, ir, mtop, ir->gb_algorithm);
 +
 +        /* Copy local gb data (for dd, this is done in dd_partition_system) */
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            make_local_gb(cr, fr->born, ir->gb_algorithm);
 +        }
 +    }
 +
 +    /* Set the charge scaling */
 +    if (fr->epsilon_r != 0)
 +    {
 +        fr->epsfac = ONE_4PI_EPS0/fr->epsilon_r;
 +    }
 +    else
 +    {
 +        /* eps = 0 is infinite dieletric: no coulomb interactions */
 +        fr->epsfac = 0;
 +    }
 +
 +    /* Reaction field constants */
 +    if (EEL_RF(fr->eeltype))
 +    {
 +        calc_rffac(fp, fr->eeltype, fr->epsilon_r, fr->epsilon_rf,
 +                   fr->rcoulomb, fr->temp, fr->zsquare, box,
 +                   &fr->kappa, &fr->k_rf, &fr->c_rf);
 +    }
 +
 +    /*This now calculates sum for q and c6*/
 +    set_chargesum(fp, fr, mtop);
 +
 +    /* if we are using LR electrostatics, and they are tabulated,
 +     * the tables will contain modified coulomb interactions.
 +     * Since we want to use the non-shifted ones for 1-4
 +     * coulombic interactions, we must have an extra set of tables.
 +     */
 +
 +    /* Construct tables.
 +     * A little unnecessary to make both vdw and coul tables sometimes,
 +     * but what the heck... */
 +
 +    bMakeTables = fr->bcoultab || fr->bvdwtab || fr->bEwald ||
 +        (ir->eDispCorr != edispcNO && ir_vdw_switched(ir));
 +
 +    bMakeSeparate14Table = ((!bMakeTables || fr->eeltype != eelCUT || fr->vdwtype != evdwCUT ||
 +                             fr->bBHAM || fr->bEwald) &&
 +                            (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 ||
 +                             gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0 ||
 +                             gmx_mtop_ftype_count(mtop, F_LJC_PAIRS_NB) > 0));
 +
 +    negp_pp   = ir->opts.ngener - ir->nwall;
 +    negptable = 0;
 +    if (!bMakeTables)
 +    {
 +        bSomeNormalNbListsAreInUse = TRUE;
 +        fr->nnblists               = 1;
 +    }
 +    else
 +    {
 +        bSomeNormalNbListsAreInUse = (ir->eDispCorr != edispcNO);
 +        for (egi = 0; egi < negp_pp; egi++)
 +        {
 +            for (egj = egi; egj < negp_pp; egj++)
 +            {
 +                egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)];
 +                if (!(egp_flags & EGP_EXCL))
 +                {
 +                    if (egp_flags & EGP_TABLE)
 +                    {
 +                        negptable++;
 +                    }
 +                    else
 +                    {
 +                        bSomeNormalNbListsAreInUse = TRUE;
 +                    }
 +                }
 +            }
 +        }
 +        if (bSomeNormalNbListsAreInUse)
 +        {
 +            fr->nnblists = negptable + 1;
 +        }
 +        else
 +        {
 +            fr->nnblists = negptable;
 +        }
 +        if (fr->nnblists > 1)
 +        {
 +            snew(fr->gid2nblists, ir->opts.ngener*ir->opts.ngener);
 +        }
 +    }
 +
 +    if (ir->adress)
 +    {
 +        fr->nnblists *= 2;
 +    }
 +
 +    snew(fr->nblists, fr->nnblists);
 +
 +    /* This code automatically gives table length tabext without cut-off's,
 +     * in that case grompp should already have checked that we do not need
 +     * normal tables and we only generate tables for 1-4 interactions.
 +     */
 +    rtab = ir->rlistlong + ir->tabext;
 +
 +    if (bMakeTables)
 +    {
 +        /* make tables for ordinary interactions */
 +        if (bSomeNormalNbListsAreInUse)
 +        {
 +            make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[0]);
 +            if (ir->adress)
 +            {
 +                make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[fr->nnblists/2]);
 +            }
 +            if (!bMakeSeparate14Table)
 +            {
 +                fr->tab14 = fr->nblists[0].table_elec_vdw;
 +            }
 +            m = 1;
 +        }
 +        else
 +        {
 +            m = 0;
 +        }
 +        if (negptable > 0)
 +        {
 +            /* Read the special tables for certain energy group pairs */
 +            nm_ind = mtop->groups.grps[egcENER].nm_ind;
 +            for (egi = 0; egi < negp_pp; egi++)
 +            {
 +                for (egj = egi; egj < negp_pp; egj++)
 +                {
 +                    egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)];
 +                    if ((egp_flags & EGP_TABLE) && !(egp_flags & EGP_EXCL))
 +                    {
 +                        nbl = &(fr->nblists[m]);
 +                        if (fr->nnblists > 1)
 +                        {
 +                            fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = m;
 +                        }
 +                        /* Read the table file with the two energy groups names appended */
 +                        make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn,
 +                                        *mtop->groups.grpname[nm_ind[egi]],
 +                                        *mtop->groups.grpname[nm_ind[egj]],
 +                                        &fr->nblists[m]);
 +                        if (ir->adress)
 +                        {
 +                            make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn,
 +                                            *mtop->groups.grpname[nm_ind[egi]],
 +                                            *mtop->groups.grpname[nm_ind[egj]],
 +                                            &fr->nblists[fr->nnblists/2+m]);
 +                        }
 +                        m++;
 +                    }
 +                    else if (fr->nnblists > 1)
 +                    {
 +                        fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = 0;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (bMakeSeparate14Table)
 +    {
 +        /* generate extra tables with plain Coulomb for 1-4 interactions only */
 +        fr->tab14 = make_tables(fp, oenv, fr, MASTER(cr), tabpfn, rtab,
 +                                GMX_MAKETABLES_14ONLY);
 +    }
 +
 +    /* Read AdResS Thermo Force table if needed */
 +    if (fr->adress_icor == eAdressICThermoForce)
 +    {
 +        /* old todo replace */
 +
 +        if (ir->adress->n_tf_grps > 0)
 +        {
 +            make_adress_tf_tables(fp, oenv, fr, ir, tabfn, mtop, box);
 +
 +        }
 +        else
 +        {
 +            /* load the default table */
 +            snew(fr->atf_tabs, 1);
 +            fr->atf_tabs[DEFAULT_TF_TABLE] = make_atf_table(fp, oenv, fr, tabafn, box);
 +        }
 +    }
 +
 +    /* Wall stuff */
 +    fr->nwall = ir->nwall;
 +    if (ir->nwall && ir->wall_type == ewtTABLE)
 +    {
 +        make_wall_tables(fp, oenv, ir, tabfn, &mtop->groups, fr);
 +    }
 +
 +    if (fcd && tabbfn)
 +    {
 +        fcd->bondtab  = make_bonded_tables(fp,
 +                                           F_TABBONDS, F_TABBONDSNC,
 +                                           mtop, tabbfn, "b");
 +        fcd->angletab = make_bonded_tables(fp,
 +                                           F_TABANGLES, -1,
 +                                           mtop, tabbfn, "a");
 +        fcd->dihtab   = make_bonded_tables(fp,
 +                                           F_TABDIHS, -1,
 +                                           mtop, tabbfn, "d");
 +    }
 +    else
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "No fcdata or table file name passed, can not read table, can not do bonded interactions\n");
 +        }
 +    }
 +
 +    /* QM/MM initialization if requested
 +     */
 +    if (ir->bQMMM)
 +    {
 +        fprintf(stderr, "QM/MM calculation requested.\n");
 +    }
 +
 +    fr->bQMMM      = ir->bQMMM;
 +    fr->qr         = mk_QMMMrec();
 +
 +    /* Set all the static charge group info */
 +    fr->cginfo_mb = init_cginfo_mb(fp, mtop, fr, bNoSolvOpt,
 +                                   &bFEP_NonBonded,
 +                                   &fr->bExcl_IntraCGAll_InterCGNone);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        fr->cginfo = NULL;
 +    }
 +    else
 +    {
 +        fr->cginfo = cginfo_expand(mtop->nmolblock, fr->cginfo_mb);
 +    }
 +
 +    if (!DOMAINDECOMP(cr))
 +    {
 +        forcerec_set_ranges(fr, ncg_mtop(mtop), ncg_mtop(mtop),
 +                            mtop->natoms, mtop->natoms, mtop->natoms);
 +    }
 +
 +    fr->print_force = print_force;
 +
 +
 +    /* coarse load balancing vars */
 +    fr->t_fnbf    = 0.;
 +    fr->t_wait    = 0.;
 +    fr->timesteps = 0;
 +
 +    /* Initialize neighbor search */
 +    init_ns(fp, cr, &fr->ns, fr, mtop);
 +
 +    if (cr->duty & DUTY_PP)
 +    {
 +        gmx_nonbonded_setup(fr, bGenericKernelOnly);
 +        /*
 +           if (ir->bAdress)
 +            {
 +                gmx_setup_adress_kernels(fp,bGenericKernelOnly);
 +            }
 +         */
 +    }
 +
 +    /* Initialize the thread working data for bonded interactions */
 +    init_forcerec_f_threads(fr, mtop->groups.grps[egcENER].nr);
 +
 +    snew(fr->excl_load, fr->nthreads+1);
 +
 +    if (fr->cutoff_scheme == ecutsVERLET)
 +    {
 +        if (ir->rcoulomb != ir->rvdw)
 +        {
 +            gmx_fatal(FARGS, "With Verlet lists rcoulomb and rvdw should be identical");
 +        }
 +
 +        init_nb_verlet(fp, &fr->nbv, bFEP_NonBonded, ir, fr, cr, nbpu_opt);
 +    }
 +
 +    /* fr->ic is used both by verlet and group kernels (to some extent) now */
 +    init_interaction_const(fp, cr, &fr->ic, fr, rtab);
 +
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        calc_enervirdiff(fp, ir->eDispCorr, fr);
 +    }
 +}
 +
 +#define pr_real(fp, r) fprintf(fp, "%s: %e\n",#r, r)
 +#define pr_int(fp, i)  fprintf((fp), "%s: %d\n",#i, i)
 +#define pr_bool(fp, b) fprintf((fp), "%s: %s\n",#b, bool_names[b])
 +
 +void pr_forcerec(FILE *fp, t_forcerec *fr)
 +{
 +    int i;
 +
 +    pr_real(fp, fr->rlist);
 +    pr_real(fp, fr->rcoulomb);
 +    pr_real(fp, fr->fudgeQQ);
 +    pr_bool(fp, fr->bGrid);
 +    pr_bool(fp, fr->bTwinRange);
 +    /*pr_int(fp,fr->cg0);
 +       pr_int(fp,fr->hcg);*/
 +    for (i = 0; i < fr->nnblists; i++)
 +    {
 +        pr_int(fp, fr->nblists[i].table_elec_vdw.n);
 +    }
 +    pr_real(fp, fr->rcoulomb_switch);
 +    pr_real(fp, fr->rcoulomb);
 +
 +    fflush(fp);
 +}
 +
 +void forcerec_set_excl_load(t_forcerec           *fr,
 +                            const gmx_localtop_t *top)
 +{
 +    const int *ind, *a;
 +    int        t, i, j, ntot, n, ntarget;
 +
 +    ind = top->excls.index;
 +    a   = top->excls.a;
 +
 +    ntot = 0;
 +    for (i = 0; i < top->excls.nr; i++)
 +    {
 +        for (j = ind[i]; j < ind[i+1]; j++)
 +        {
 +            if (a[j] > i)
 +            {
 +                ntot++;
 +            }
 +        }
 +    }
 +
 +    fr->excl_load[0] = 0;
 +    n                = 0;
 +    i                = 0;
 +    for (t = 1; t <= fr->nthreads; t++)
 +    {
 +        ntarget = (ntot*t)/fr->nthreads;
 +        while (i < top->excls.nr && n < ntarget)
 +        {
 +            for (j = ind[i]; j < ind[i+1]; j++)
 +            {
 +                if (a[j] > i)
 +                {
 +                    n++;
 +                }
 +            }
 +            i++;
 +        }
 +        fr->excl_load[t] = i;
 +    }
 +}
index 1558e45fa49755ad9f560a6334fbcb0cc841dff2,0000000000000000000000000000000000000000..748768687bb94b1a5bb9cd5c3ced4ddf6cd88e45
mode 100644,000000..100644
--- /dev/null
@@@ -1,765 -1,0 +1,771 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "mdrun.h"
 +#include "domdec.h"
 +#include "mtop_util.h"
 +#include "vcm.h"
 +#include "nrnb.h"
 +#include "macros.h"
 +#include "md_logging.h"
 +#include "md_support.h"
++#include "names.h"
 +
 +#include "gromacs/timing/wallcycle.h"
 +
 +/* Is the signal in one simulation independent of other simulations? */
 +gmx_bool gs_simlocal[eglsNR] = { TRUE, FALSE, FALSE, TRUE };
 +
 +/* check which of the multisim simulations has the shortest number of
 +   steps and return that number of nsteps */
 +gmx_int64_t get_multisim_nsteps(const t_commrec *cr,
 +                                gmx_int64_t      nsteps)
 +{
 +    gmx_int64_t steps_out;
 +
 +    if (MASTER(cr))
 +    {
 +        gmx_int64_t     *buf;
 +        int              s;
 +
 +        snew(buf, cr->ms->nsim);
 +
 +        buf[cr->ms->sim] = nsteps;
 +        gmx_sumli_sim(cr->ms->nsim, buf, cr->ms);
 +
 +        steps_out = -1;
 +        for (s = 0; s < cr->ms->nsim; s++)
 +        {
 +            /* find the smallest positive number */
 +            if (buf[s] >= 0 && ((steps_out < 0) || (buf[s] < steps_out)) )
 +            {
 +                steps_out = buf[s];
 +            }
 +        }
 +        sfree(buf);
 +
 +        /* if we're the limiting simulation, don't do anything */
 +        if (steps_out >= 0 && steps_out < nsteps)
 +        {
 +            char strbuf[255];
 +            snprintf(strbuf, 255, "Will stop simulation %%d after %s steps (another simulation will end then).\n", "%"GMX_PRId64);
 +            fprintf(stderr, strbuf, cr->ms->sim, steps_out);
 +        }
 +    }
 +    /* broadcast to non-masters */
 +    gmx_bcast(sizeof(gmx_int64_t), &steps_out, cr);
 +    return steps_out;
 +}
 +
 +int multisim_min(const gmx_multisim_t *ms, int nmin, int n)
 +{
 +    int     *buf;
 +    gmx_bool bPos, bEqual;
 +    int      s, d;
 +
 +    snew(buf, ms->nsim);
 +    buf[ms->sim] = n;
 +    gmx_sumi_sim(ms->nsim, buf, ms);
 +    bPos   = TRUE;
 +    bEqual = TRUE;
 +    for (s = 0; s < ms->nsim; s++)
 +    {
 +        bPos   = bPos   && (buf[s] > 0);
 +        bEqual = bEqual && (buf[s] == buf[0]);
 +    }
 +    if (bPos)
 +    {
 +        if (bEqual)
 +        {
 +            nmin = min(nmin, buf[0]);
 +        }
 +        else
 +        {
 +            /* Find the least common multiple */
 +            for (d = 2; d < nmin; d++)
 +            {
 +                s = 0;
 +                while (s < ms->nsim && d % buf[s] == 0)
 +                {
 +                    s++;
 +                }
 +                if (s == ms->nsim)
 +                {
 +                    /* We found the LCM and it is less than nmin */
 +                    nmin = d;
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +    sfree(buf);
 +
 +    return nmin;
 +}
 +
 +int multisim_nstsimsync(const t_commrec *cr,
 +                        const t_inputrec *ir, int repl_ex_nst)
 +{
 +    int nmin;
 +
 +    if (MASTER(cr))
 +    {
 +        nmin = INT_MAX;
 +        nmin = multisim_min(cr->ms, nmin, ir->nstlist);
 +        nmin = multisim_min(cr->ms, nmin, ir->nstcalcenergy);
 +        nmin = multisim_min(cr->ms, nmin, repl_ex_nst);
 +        if (nmin == INT_MAX)
 +        {
 +            gmx_fatal(FARGS, "Can not find an appropriate interval for inter-simulation communication, since nstlist, nstcalcenergy and -replex are all <= 0");
 +        }
 +        /* Avoid inter-simulation communication at every (second) step */
 +        if (nmin <= 2)
 +        {
 +            nmin = 10;
 +        }
 +    }
 +
 +    gmx_bcast(sizeof(int), &nmin, cr);
 +
 +    return nmin;
 +}
 +
 +void init_global_signals(globsig_t *gs, const t_commrec *cr,
 +                         const t_inputrec *ir, int repl_ex_nst)
 +{
 +    int i;
 +
 +    if (MULTISIM(cr))
 +    {
 +        gs->nstms = multisim_nstsimsync(cr, ir, repl_ex_nst);
 +        if (debug)
 +        {
 +            fprintf(debug, "Syncing simulations for checkpointing and termination every %d steps\n", gs->nstms);
 +        }
 +    }
 +    else
 +    {
 +        gs->nstms = 1;
 +    }
 +
 +    for (i = 0; i < eglsNR; i++)
 +    {
 +        gs->sig[i] = 0;
 +        gs->set[i] = 0;
 +    }
 +}
 +
 +void copy_coupling_state(t_state *statea, t_state *stateb,
 +                         gmx_ekindata_t *ekinda, gmx_ekindata_t *ekindb, t_grpopts* opts)
 +{
 +
 +    /* MRS note -- might be able to get rid of some of the arguments.  Look over it when it's all debugged */
 +
 +    int i, j, nc;
 +
 +    /* Make sure we have enough space for x and v */
 +    if (statea->nalloc > stateb->nalloc)
 +    {
 +        stateb->nalloc = statea->nalloc;
 +        srenew(stateb->x, stateb->nalloc);
 +        srenew(stateb->v, stateb->nalloc);
 +    }
 +
 +    stateb->natoms     = statea->natoms;
 +    stateb->ngtc       = statea->ngtc;
 +    stateb->nnhpres    = statea->nnhpres;
 +    stateb->veta       = statea->veta;
 +    if (ekinda)
 +    {
 +        copy_mat(ekinda->ekin, ekindb->ekin);
 +        for (i = 0; i < stateb->ngtc; i++)
 +        {
 +            ekindb->tcstat[i].T  = ekinda->tcstat[i].T;
 +            ekindb->tcstat[i].Th = ekinda->tcstat[i].Th;
 +            copy_mat(ekinda->tcstat[i].ekinh, ekindb->tcstat[i].ekinh);
 +            copy_mat(ekinda->tcstat[i].ekinf, ekindb->tcstat[i].ekinf);
 +            ekindb->tcstat[i].ekinscalef_nhc =  ekinda->tcstat[i].ekinscalef_nhc;
 +            ekindb->tcstat[i].ekinscaleh_nhc =  ekinda->tcstat[i].ekinscaleh_nhc;
 +            ekindb->tcstat[i].vscale_nhc     =  ekinda->tcstat[i].vscale_nhc;
 +        }
 +    }
 +    copy_rvecn(statea->x, stateb->x, 0, stateb->natoms);
 +    copy_rvecn(statea->v, stateb->v, 0, stateb->natoms);
 +    copy_mat(statea->box, stateb->box);
 +    copy_mat(statea->box_rel, stateb->box_rel);
 +    copy_mat(statea->boxv, stateb->boxv);
 +
 +    for (i = 0; i < stateb->ngtc; i++)
 +    {
 +        nc = i*opts->nhchainlength;
 +        for (j = 0; j < opts->nhchainlength; j++)
 +        {
 +            stateb->nosehoover_xi[nc+j]  = statea->nosehoover_xi[nc+j];
 +            stateb->nosehoover_vxi[nc+j] = statea->nosehoover_vxi[nc+j];
 +        }
 +    }
 +    if (stateb->nhpres_xi != NULL)
 +    {
 +        for (i = 0; i < stateb->nnhpres; i++)
 +        {
 +            nc = i*opts->nhchainlength;
 +            for (j = 0; j < opts->nhchainlength; j++)
 +            {
 +                stateb->nhpres_xi[nc+j]  = statea->nhpres_xi[nc+j];
 +                stateb->nhpres_vxi[nc+j] = statea->nhpres_vxi[nc+j];
 +            }
 +        }
 +    }
 +}
 +
 +real compute_conserved_from_auxiliary(t_inputrec *ir, t_state *state, t_extmass *MassQ)
 +{
 +    real quantity = 0;
 +    switch (ir->etc)
 +    {
 +        case etcNO:
 +            break;
 +        case etcBERENDSEN:
 +            break;
 +        case etcNOSEHOOVER:
 +            quantity = NPT_energy(ir, state, MassQ);
 +            break;
 +        case etcVRESCALE:
 +            quantity = vrescale_energy(&(ir->opts), state->therm_integral);
 +            break;
 +        default:
 +            break;
 +    }
 +    return quantity;
 +}
 +
 +void compute_globals(FILE *fplog, gmx_global_stat_t gstat, t_commrec *cr, t_inputrec *ir,
 +                     t_forcerec *fr, gmx_ekindata_t *ekind,
 +                     t_state *state, t_state *state_global, t_mdatoms *mdatoms,
 +                     t_nrnb *nrnb, t_vcm *vcm, gmx_wallcycle_t wcycle,
 +                     gmx_enerdata_t *enerd, tensor force_vir, tensor shake_vir, tensor total_vir,
 +                     tensor pres, rvec mu_tot, gmx_constr_t constr,
 +                     globsig_t *gs, gmx_bool bInterSimGS,
 +                     matrix box, gmx_mtop_t *top_global,
 +                     gmx_bool *bSumEkinhOld, int flags)
 +{
 +    int      i, gsi;
 +    real     gs_buf[eglsNR];
 +    tensor   corr_vir, corr_pres;
 +    gmx_bool bEner, bPres, bTemp, bVV;
 +    gmx_bool bRerunMD, bStopCM, bGStat, bIterate,
 +             bFirstIterate, bReadEkin, bEkinAveVel, bScaleEkin, bConstrain;
 +    real     ekin, temp, prescorr, enercorr, dvdlcorr, dvdl_ekin;
 +
 +    /* translate CGLO flags to gmx_booleans */
 +    bRerunMD = flags & CGLO_RERUNMD;
 +    bStopCM  = flags & CGLO_STOPCM;
 +    bGStat   = flags & CGLO_GSTAT;
 +
 +    bReadEkin     = (flags & CGLO_READEKIN);
 +    bScaleEkin    = (flags & CGLO_SCALEEKIN);
 +    bEner         = flags & CGLO_ENERGY;
 +    bTemp         = flags & CGLO_TEMPERATURE;
 +    bPres         = (flags & CGLO_PRESSURE);
 +    bConstrain    = (flags & CGLO_CONSTRAINT);
 +    bIterate      = (flags & CGLO_ITERATE);
 +    bFirstIterate = (flags & CGLO_FIRSTITERATE);
 +
 +    /* we calculate a full state kinetic energy either with full-step velocity verlet
 +       or half step where we need the pressure */
 +
 +    bEkinAveVel = (ir->eI == eiVV || (ir->eI == eiVVAK && bPres) || bReadEkin);
 +
 +    /* in initalization, it sums the shake virial in vv, and to
 +       sums ekinh_old in leapfrog (or if we are calculating ekinh_old) for other reasons */
 +
 +    /* ########## Kinetic energy  ############## */
 +
 +    if (bTemp)
 +    {
 +        /* Non-equilibrium MD: this is parallellized, but only does communication
 +         * when there really is NEMD.
 +         */
 +
 +        if (PAR(cr) && (ekind->bNEMD))
 +        {
 +            accumulate_u(cr, &(ir->opts), ekind);
 +        }
 +        debug_gmx();
 +        if (bReadEkin)
 +        {
 +            restore_ekinstate_from_state(cr, ekind, &state_global->ekinstate);
 +        }
 +        else
 +        {
 +
 +            calc_ke_part(state, &(ir->opts), mdatoms, ekind, nrnb, bEkinAveVel, bIterate);
 +        }
 +
 +        debug_gmx();
 +    }
 +
 +    /* Calculate center of mass velocity if necessary, also parallellized */
 +    if (bStopCM)
 +    {
 +        calc_vcm_grp(0, mdatoms->homenr, mdatoms,
 +                     state->x, state->v, vcm);
 +    }
 +
 +    if (bTemp || bStopCM || bPres || bEner || bConstrain)
 +    {
 +        if (!bGStat)
 +        {
 +            /* We will not sum ekinh_old,
 +             * so signal that we still have to do it.
 +             */
 +            *bSumEkinhOld = TRUE;
 +
 +        }
 +        else
 +        {
 +            if (gs != NULL)
 +            {
 +                for (i = 0; i < eglsNR; i++)
 +                {
 +                    gs_buf[i] = gs->sig[i];
 +                }
 +            }
 +            if (PAR(cr))
 +            {
 +                wallcycle_start(wcycle, ewcMoveE);
 +                global_stat(fplog, gstat, cr, enerd, force_vir, shake_vir, mu_tot,
 +                            ir, ekind, constr, bStopCM ? vcm : NULL,
 +                            gs != NULL ? eglsNR : 0, gs_buf,
 +                            top_global, state,
 +                            *bSumEkinhOld, flags);
 +                wallcycle_stop(wcycle, ewcMoveE);
 +            }
 +            if (gs != NULL)
 +            {
 +                if (MULTISIM(cr) && bInterSimGS)
 +                {
 +                    if (MASTER(cr))
 +                    {
 +                        /* Communicate the signals between the simulations */
 +                        gmx_sum_sim(eglsNR, gs_buf, cr->ms);
 +                    }
 +                    /* Communicate the signals form the master to the others */
 +                    gmx_bcast(eglsNR*sizeof(gs_buf[0]), gs_buf, cr);
 +                }
 +                for (i = 0; i < eglsNR; i++)
 +                {
 +                    if (bInterSimGS || gs_simlocal[i])
 +                    {
 +                        /* Set the communicated signal only when it is non-zero,
 +                         * since signals might not be processed at each MD step.
 +                         */
 +                        gsi = (gs_buf[i] >= 0 ?
 +                               (int)(gs_buf[i] + 0.5) :
 +                               (int)(gs_buf[i] - 0.5));
 +                        if (gsi != 0)
 +                        {
 +                            gs->set[i] = gsi;
 +                        }
 +                        /* Turn off the local signal */
 +                        gs->sig[i] = 0;
 +                    }
 +                }
 +            }
 +            *bSumEkinhOld = FALSE;
 +        }
 +    }
 +
 +    if (!ekind->bNEMD && debug && bTemp && (vcm->nr > 0))
 +    {
 +        correct_ekin(debug,
 +                     0, mdatoms->homenr,
 +                     state->v, vcm->group_p[0],
 +                     mdatoms->massT, mdatoms->tmass, ekind->ekin);
 +    }
 +
 +    /* Do center of mass motion removal */
 +    if (bStopCM)
 +    {
 +        check_cm_grp(fplog, vcm, ir, 1);
 +        do_stopcm_grp(0, mdatoms->homenr, mdatoms->cVCM,
 +                      state->x, state->v, vcm);
 +        inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr);
 +    }
 +
 +    if (bEner)
 +    {
 +        /* Calculate the amplitude of the cosine velocity profile */
 +        ekind->cosacc.vcos = ekind->cosacc.mvcos/mdatoms->tmass;
 +    }
 +
 +    if (bTemp)
 +    {
 +        /* Sum the kinetic energies of the groups & calc temp */
 +        /* compute full step kinetic energies if vv, or if vv-avek and we are computing the pressure with IR_NPT_TROTTER */
 +        /* three maincase:  VV with AveVel (md-vv), vv with AveEkin (md-vv-avek), leap with AveEkin (md).
 +           Leap with AveVel is not supported; it's not clear that it will actually work.
 +           bEkinAveVel: If TRUE, we simply multiply ekin by ekinscale to get a full step kinetic energy.
 +           If FALSE, we average ekinh_old and ekinh*ekinscale_nhc to get an averaged half step kinetic energy.
 +           bSaveEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't reset the ekinscale_nhc.
 +           If FALSE, we go ahead and erase over it.
 +         */
 +        enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, &dvdl_ekin,
 +                                       bEkinAveVel, bScaleEkin);
 +        enerd->dvdl_lin[efptMASS] = (double) dvdl_ekin;
 +
 +        enerd->term[F_EKIN] = trace(ekind->ekin);
 +    }
 +
 +    /* ##########  Long range energy information ###### */
 +
 +    if (bEner || bPres || bConstrain)
 +    {
 +        calc_dispcorr(fplog, ir, fr, 0, top_global->natoms, box, state->lambda[efptVDW],
 +                      corr_pres, corr_vir, &prescorr, &enercorr, &dvdlcorr);
 +    }
 +
 +    if (bEner && bFirstIterate)
 +    {
 +        enerd->term[F_DISPCORR]  = enercorr;
 +        enerd->term[F_EPOT]     += enercorr;
 +        enerd->term[F_DVDL_VDW] += dvdlcorr;
 +    }
 +
 +    /* ########## Now pressure ############## */
 +    if (bPres || bConstrain)
 +    {
 +
 +        m_add(force_vir, shake_vir, total_vir);
 +
 +        /* Calculate pressure and apply LR correction if PPPM is used.
 +         * Use the box from last timestep since we already called update().
 +         */
 +
 +        enerd->term[F_PRES] = calc_pres(fr->ePBC, ir->nwall, box, ekind->ekin, total_vir, pres);
 +
 +        /* Calculate long range corrections to pressure and energy */
 +        /* this adds to enerd->term[F_PRES] and enerd->term[F_ETOT],
 +           and computes enerd->term[F_DISPCORR].  Also modifies the
 +           total_vir and pres tesors */
 +
 +        m_add(total_vir, corr_vir, total_vir);
 +        m_add(pres, corr_pres, pres);
 +        enerd->term[F_PDISPCORR] = prescorr;
 +        enerd->term[F_PRES]     += prescorr;
 +    }
 +}
 +
 +void check_nst_param(FILE *fplog, t_commrec *cr,
 +                     const char *desc_nst, int nst,
 +                     const char *desc_p, int *p)
 +{
 +    if (*p > 0 && *p % nst != 0)
 +    {
 +        /* Round up to the next multiple of nst */
 +        *p = ((*p)/nst + 1)*nst;
 +        md_print_warn(cr, fplog,
 +                      "NOTE: %s changes %s to %d\n", desc_nst, desc_p, *p);
 +    }
 +}
 +
 +void set_current_lambdas(gmx_int64_t step, t_lambda *fepvals, gmx_bool bRerunMD,
 +                         t_trxframe *rerun_fr, t_state *state_global, t_state *state, double lam0[])
 +/* find the current lambdas.  If rerunning, we either read in a state, or a lambda value,
 +   requiring different logic. */
 +{
 +    real frac;
 +    int  i, fep_state = 0;
 +    if (bRerunMD)
 +    {
 +        if (rerun_fr->bLambda)
 +        {
 +            if (fepvals->delta_lambda == 0)
 +            {
 +                state_global->lambda[efptFEP] = rerun_fr->lambda;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    if (i != efptFEP)
 +                    {
 +                        state->lambda[i] = state_global->lambda[i];
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                /* find out between which two value of lambda we should be */
 +                frac      = (step*fepvals->delta_lambda);
 +                fep_state = floor(frac*fepvals->n_lambda);
 +                /* interpolate between this state and the next */
 +                /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
 +                frac = (frac*fepvals->n_lambda)-fep_state;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) +
 +                        frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]);
 +                }
 +            }
 +        }
 +        else if (rerun_fr->bFepState)
 +        {
 +            state_global->fep_state = rerun_fr->fep_state;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                state_global->lambda[i] = fepvals->all_lambda[i][fep_state];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        if (fepvals->delta_lambda != 0)
 +        {
 +            /* find out between which two value of lambda we should be */
 +            frac = (step*fepvals->delta_lambda);
 +            if (fepvals->n_lambda > 0)
 +            {
 +                fep_state = floor(frac*fepvals->n_lambda);
 +                /* interpolate between this state and the next */
 +                /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
 +                frac = (frac*fepvals->n_lambda)-fep_state;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) +
 +                        frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + frac;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            if (state->fep_state > 0)
 +            {
 +                state_global->fep_state = state->fep_state; /* state->fep is the one updated by bExpanded */
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = fepvals->all_lambda[i][state_global->fep_state];
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        state->lambda[i] = state_global->lambda[i];
 +    }
 +}
 +
 +static void min_zero(int *n, int i)
 +{
 +    if (i > 0 && (*n == 0 || i < *n))
 +    {
 +        *n = i;
 +    }
 +}
 +
 +static int lcd4(int i1, int i2, int i3, int i4)
 +{
 +    int nst;
 +
 +    nst = 0;
 +    min_zero(&nst, i1);
 +    min_zero(&nst, i2);
 +    min_zero(&nst, i3);
 +    min_zero(&nst, i4);
 +    if (nst == 0)
 +    {
 +        gmx_incons("All 4 inputs for determininig nstglobalcomm are <= 0");
 +    }
 +
 +    while (nst > 1 && ((i1 > 0 && i1 % nst != 0)  ||
 +                       (i2 > 0 && i2 % nst != 0)  ||
 +                       (i3 > 0 && i3 % nst != 0)  ||
 +                       (i4 > 0 && i4 % nst != 0)))
 +    {
 +        nst--;
 +    }
 +
 +    return nst;
 +}
 +
 +int check_nstglobalcomm(FILE *fplog, t_commrec *cr,
 +                        int nstglobalcomm, t_inputrec *ir)
 +{
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        nstglobalcomm = 1;
 +    }
 +
 +    if (nstglobalcomm == -1)
 +    {
 +        if (!(ir->nstcalcenergy > 0 ||
 +              ir->nstlist > 0 ||
 +              ir->etc != etcNO ||
 +              ir->epc != epcNO))
 +        {
 +            nstglobalcomm = 10;
 +            if (ir->nstenergy > 0 && ir->nstenergy < nstglobalcomm)
 +            {
 +                nstglobalcomm = ir->nstenergy;
 +            }
 +        }
 +        else
 +        {
 +            /* Ensure that we do timely global communication for
 +             * (possibly) each of the four following options.
 +             */
 +            nstglobalcomm = lcd4(ir->nstcalcenergy,
 +                                 ir->nstlist,
 +                                 ir->etc != etcNO ? ir->nsttcouple : 0,
 +                                 ir->epc != epcNO ? ir->nstpcouple : 0);
 +        }
 +    }
 +    else
 +    {
 +        if (ir->nstlist > 0 &&
 +            nstglobalcomm > ir->nstlist && nstglobalcomm % ir->nstlist != 0)
 +        {
 +            nstglobalcomm = (nstglobalcomm / ir->nstlist)*ir->nstlist;
 +            md_print_warn(cr, fplog, "WARNING: nstglobalcomm is larger than nstlist, but not a multiple, setting it to %d\n", nstglobalcomm);
 +        }
 +        if (ir->nstcalcenergy > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nstcalcenergy", &ir->nstcalcenergy);
 +        }
 +        if (ir->etc != etcNO && ir->nsttcouple > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nsttcouple", &ir->nsttcouple);
 +        }
 +        if (ir->epc != epcNO && ir->nstpcouple > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nstpcouple", &ir->nstpcouple);
 +        }
 +
 +        check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                        "nstenergy", &ir->nstenergy);
 +
 +        check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                        "nstlog", &ir->nstlog);
 +    }
 +
 +    if (ir->comm_mode != ecmNO && ir->nstcomm < nstglobalcomm)
 +    {
 +        md_print_warn(cr, fplog, "WARNING: Changing nstcomm from %d to %d\n",
 +                      ir->nstcomm, nstglobalcomm);
 +        ir->nstcomm = nstglobalcomm;
 +    }
 +
 +    return nstglobalcomm;
 +}
 +
 +void check_ir_old_tpx_versions(t_commrec *cr, FILE *fplog,
 +                               t_inputrec *ir, gmx_mtop_t *mtop)
 +{
 +    /* Check required for old tpx files */
 +    if (IR_TWINRANGE(*ir) && ir->nstlist > 1 &&
 +        ir->nstcalcenergy % ir->nstlist != 0)
 +    {
 +        md_print_warn(cr, fplog, "Old tpr file with twin-range settings: modifying energy calculation and/or T/P-coupling frequencies\n");
 +
 +        if (gmx_mtop_ftype_count(mtop, F_CONSTR) +
 +            gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0 &&
 +            ir->eConstrAlg == econtSHAKE)
 +        {
 +            md_print_warn(cr, fplog, "With twin-range cut-off's and SHAKE the virial and pressure are incorrect\n");
 +            if (ir->epc != epcNO)
 +            {
 +                gmx_fatal(FARGS, "Can not do pressure coupling with twin-range cut-off's and SHAKE");
 +            }
 +        }
 +        check_nst_param(fplog, cr, "nstlist", ir->nstlist,
 +                        "nstcalcenergy", &ir->nstcalcenergy);
 +        if (ir->epc != epcNO)
 +        {
 +            check_nst_param(fplog, cr, "nstlist", ir->nstlist,
 +                            "nstpcouple", &ir->nstpcouple);
 +        }
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "nstenergy", &ir->nstenergy);
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "nstlog", &ir->nstlog);
 +        if (ir->efep != efepNO)
 +        {
 +            check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                            "nstdhdl", &ir->fepvals->nstdhdl);
 +        }
 +    }
++
++    if (EI_VV(ir->eI) && IR_TWINRANGE(*ir) && ir->nstlist > 1)
++    {
++        gmx_fatal(FARGS, "Twin-range multiple time stepping does not work with integrator %s.", ei_names[ir->eI]);
++    }
 +}
 +
 +void rerun_parallel_comm(t_commrec *cr, t_trxframe *fr,
 +                         gmx_bool *bNotLastFrame)
 +{
 +    gmx_bool bAlloc;
 +    rvec    *xp, *vp;
 +
 +    bAlloc = (fr->natoms == 0);
 +
 +    if (MASTER(cr) && !*bNotLastFrame)
 +    {
 +        fr->natoms = -1;
 +    }
 +    xp = fr->x;
 +    vp = fr->v;
 +    gmx_bcast(sizeof(*fr), fr, cr);
 +    fr->x = xp;
 +    fr->v = vp;
 +
 +    *bNotLastFrame = (fr->natoms >= 0);
 +
 +}
index b2200918f8fc8212fee8b46725a698c495e10d00,0000000000000000000000000000000000000000..a4500a4d4e24a749eb2ae0e9a0509245787190b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,1272 -1,0 +1,1272 @@@
-         gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f)",
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +
 +#include <math.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "vec.h"
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "network.h"
 +#include "gromacs/fileio/filenm.h"
 +#include <string.h>
 +#include "gromacs/utility/smalloc.h"
 +#include "pull.h"
 +#include "xvgr.h"
 +#include "names.h"
 +#include "pbc.h"
 +#include "mtop_util.h"
 +#include "mdrun.h"
 +#include "gmx_ga2la.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +#include "vec.h"
 +
 +static void pull_print_group_x(FILE *out, ivec dim, const t_pull_group *pgrp)
 +{
 +    int m;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        if (dim[m])
 +        {
 +            fprintf(out, "\t%g", pgrp->x[m]);
 +        }
 +    }
 +}
 +
 +static void pull_print_coord_dr(FILE *out, ivec dim, const t_pull_coord *pcrd)
 +{
 +    int m;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        if (dim[m])
 +        {
 +            fprintf(out, "\t%g", pcrd->dr[m]);
 +        }
 +    }
 +}
 +
 +static void pull_print_x(FILE *out, t_pull *pull, double t)
 +{
 +    int                 c;
 +    const t_pull_coord *pcrd;
 +
 +    fprintf(out, "%.4f", t);
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (pull->bPrintRef)
 +        {
 +            if (PULL_CYL(pull))
 +            {
 +                pull_print_group_x(out, pull->dim, &pull->dyna[c]);
 +            }
 +            else
 +            {
 +                pull_print_group_x(out, pull->dim, &pull->group[pcrd->group[0]]);
 +            }
 +        }
 +        pull_print_coord_dr(out, pull->dim, pcrd);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +static void pull_print_f(FILE *out, t_pull *pull, double t)
 +{
 +    int c, d;
 +
 +    fprintf(out, "%.4f", t);
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        fprintf(out, "\t%g", pull->coord[c].f_scal);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +void pull_print_output(t_pull *pull, gmx_int64_t step, double time)
 +{
 +    if ((pull->nstxout != 0) && (step % pull->nstxout == 0))
 +    {
 +        pull_print_x(pull->out_x, pull, time);
 +    }
 +
 +    if ((pull->nstfout != 0) && (step % pull->nstfout == 0))
 +    {
 +        pull_print_f(pull->out_f, pull, time);
 +    }
 +}
 +
 +static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv,
 +                           gmx_bool bCoord, unsigned long Flags)
 +{
 +    FILE  *fp;
 +    int    nsets, c, m;
 +    char **setname, buf[10];
 +
 +    if (Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a+");
 +    }
 +    else
 +    {
 +        fp = gmx_fio_fopen(fn, "w+");
 +        if (bCoord)
 +        {
 +            xvgr_header(fp, "Pull COM",  "Time (ps)", "Position (nm)",
 +                        exvggtXNY, oenv);
 +        }
 +        else
 +        {
 +            xvgr_header(fp, "Pull force", "Time (ps)", "Force (kJ/mol/nm)",
 +                        exvggtXNY, oenv);
 +        }
 +
 +        snew(setname, 2*pull->ncoord*DIM);
 +        nsets = 0;
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (bCoord)
 +            {
 +                if (pull->bPrintRef)
 +                {
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        if (pull->dim[m])
 +                        {
 +                            sprintf(buf, "%d %s%c", c+1, "c", 'X'+m);
 +                            setname[nsets] = strdup(buf);
 +                            nsets++;
 +                        }
 +                    }
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (pull->dim[m])
 +                    {
 +                        sprintf(buf, "%d %s%c", c+1, "d", 'X'+m);
 +                        setname[nsets] = strdup(buf);
 +                        nsets++;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                sprintf(buf, "%d", c+1);
 +                setname[nsets] = strdup(buf);
 +                nsets++;
 +            }
 +        }
 +        if (nsets > 1)
 +        {
 +            xvgr_legend(fp, nsets, (const char**)setname, oenv);
 +        }
 +        for (c = 0; c < nsets; c++)
 +        {
 +            sfree(setname[c]);
 +        }
 +        sfree(setname);
 +    }
 +
 +    return fp;
 +}
 +
 +/* Apply forces in a mass weighted fashion */
 +static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md,
 +                             const dvec f_pull, int sign, rvec *f)
 +{
 +    int    i, ii, m;
 +    double wmass, inv_wm;
 +
 +    inv_wm = pgrp->wscale*pgrp->invtm;
 +
 +    for (i = 0; i < pgrp->nat_loc; i++)
 +    {
 +        ii    = pgrp->ind_loc[i];
 +        wmass = md->massT[ii];
 +        if (pgrp->weight_loc)
 +        {
 +            wmass *= pgrp->weight_loc[i];
 +        }
 +
 +        for (m = 0; m < DIM; m++)
 +        {
 +            f[ii][m] += sign * wmass * f_pull[m] * inv_wm;
 +        }
 +    }
 +}
 +
 +/* Apply forces in a mass weighted fashion */
 +static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f)
 +{
 +    int                 c;
 +    const t_pull_coord *pcrd;
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (PULL_CYL(pull))
 +        {
 +            apply_forces_grp(&pull->dyna[c], md, pcrd->f, -1, f);
 +        }
 +        else
 +        {
 +            if (pull->group[pcrd->group[0]].nat > 0)
 +            {
 +                apply_forces_grp(&pull->group[pcrd->group[0]], md, pcrd->f, -1, f);
 +            }
 +        }
 +        apply_forces_grp(&pull->group[pcrd->group[1]], md, pcrd->f, 1, f);
 +    }
 +}
 +
 +static double max_pull_distance2(const t_pull *pull, const t_pbc *pbc)
 +{
 +    double max_d2;
 +    int    m;
 +
 +    max_d2 = GMX_DOUBLE_MAX;
 +
 +    if (pull->eGeom != epullgDIRPBC)
 +    {
 +        for (m = 0; m < pbc->ndim_ePBC; m++)
 +        {
 +            if (pull->dim[m] != 0)
 +            {
 +                max_d2 = min(max_d2, norm2(pbc->box[m]));
 +            }
 +        }
 +    }
 +
 +    return 0.25*max_d2;
 +}
 +
 +static void low_get_pull_coord_dr(const t_pull *pull,
 +                                  const t_pull_coord *pcrd,
 +                                  const t_pbc *pbc, double t,
 +                                  dvec xg, dvec xref, double max_dist2,
 +                                  dvec dr)
 +{
 +    const t_pull_group *pgrp0, *pgrp1;
 +    int                 m;
 +    dvec                xrefr, dref = {0, 0, 0};
 +    double              dr2;
 +
 +    pgrp0 = &pull->group[pcrd->group[0]];
 +    pgrp1 = &pull->group[pcrd->group[1]];
 +
 +    /* Only the first group can be an absolute reference, in that case nat=0 */
 +    if (pgrp0->nat == 0)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            xref[m] = pcrd->origin[m];
 +        }
 +    }
 +
 +    copy_dvec(xref, xrefr);
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dref[m] = (pcrd->init + pcrd->rate*t)*pcrd->vec[m];
 +        }
 +        /* Add the reference position, so we use the correct periodic image */
 +        dvec_inc(xrefr, dref);
 +    }
 +
 +    pbc_dx_d(pbc, xg, xrefr, dr);
 +    dr2 = 0;
 +    for (m = 0; m < DIM; m++)
 +    {
 +        dr[m] *= pull->dim[m];
 +        dr2   += dr[m]*dr[m];
 +    }
 +    if (max_dist2 >= 0 && dr2 > 0.98*0.98*max_dist2)
 +    {
++        gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\nYou might want to consider using \"pull-geometry = direction-periodic\" instead.\n",
 +                  pcrd->group[0], pcrd->group[1], sqrt(dr2), sqrt(max_dist2));
 +    }
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        dvec_inc(dr, dref);
 +    }
 +}
 +
 +static void get_pull_coord_dr(const t_pull *pull,
 +                              int coord_ind,
 +                              const t_pbc *pbc, double t,
 +                              dvec dr)
 +{
 +    double              md2;
 +    const t_pull_coord *pcrd;
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        md2 = -1;
 +    }
 +    else
 +    {
 +        md2 = max_pull_distance2(pull, pbc);
 +    }
 +
 +    pcrd = &pull->coord[coord_ind];
 +
 +    low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                          pull->group[pcrd->group[1]].x,
 +                          PULL_CYL(pull) ? pull->dyna[coord_ind].x : pull->group[pcrd->group[0]].x,
 +                          md2,
 +                          dr);
 +}
 +
 +void get_pull_coord_distance(const t_pull *pull,
 +                             int coord_ind,
 +                             const t_pbc *pbc, double t,
 +                             dvec dr, double *dev)
 +{
 +    static gmx_bool     bWarned = FALSE; /* TODO: this should be fixed for thread-safety,
 +                                            but is fairly benign */
 +    const t_pull_coord *pcrd;
 +    int                 m;
 +    double              ref, drs, inpr;
 +
 +    pcrd = &pull->coord[coord_ind];
 +
 +    get_pull_coord_dr(pull, coord_ind, pbc, t, dr);
 +
 +    ref = pcrd->init + pcrd->rate*t;
 +
 +    switch (pull->eGeom)
 +    {
 +        case epullgDIST:
 +            /* Pull along the vector between the com's */
 +            if (ref < 0 && !bWarned)
 +            {
 +                fprintf(stderr, "\nPull reference distance for coordinate %d is negative (%f)\n", coord_ind+1, ref);
 +                bWarned = TRUE;
 +            }
 +            drs = dnorm(dr);
 +            if (drs == 0)
 +            {
 +                /* With no vector we can not determine the direction for the force,
 +                 * so we set the force to zero.
 +                 */
 +                *dev = 0;
 +            }
 +            else
 +            {
 +                /* Determine the deviation */
 +                *dev = drs - ref;
 +            }
 +            break;
 +        case epullgDIR:
 +        case epullgDIRPBC:
 +        case epullgCYL:
 +            /* Pull along vec */
 +            inpr = 0;
 +            for (m = 0; m < DIM; m++)
 +            {
 +                inpr += pcrd->vec[m]*dr[m];
 +            }
 +            *dev = inpr - ref;
 +            break;
 +    }
 +}
 +
 +void clear_pull_forces(t_pull *pull)
 +{
 +    int i;
 +
 +    /* Zeroing the forces is only required for constraint pulling.
 +     * It can happen that multiple constraint steps need to be applied
 +     * and therefore the constraint forces need to be accumulated.
 +     */
 +    for (i = 0; i < pull->ncoord; i++)
 +    {
 +        clear_dvec(pull->coord[i].f);
 +        pull->coord[i].f_scal = 0;
 +    }
 +}
 +
 +/* Apply constraint using SHAKE */
 +static void do_constraint(t_pull *pull, t_pbc *pbc,
 +                          rvec *x, rvec *v,
 +                          gmx_bool bMaster, tensor vir,
 +                          double dt, double t)
 +{
 +
 +    dvec         *r_ij;   /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
 +    dvec          unc_ij; /* xp[i] com of i this step, before constr.   -> unc_ij  */
 +    dvec         *rnew;   /* current 'new' positions of the groups */
 +    double       *dr_tot; /* the total update of the coords */
 +    double        ref;
 +    dvec          vec;
 +    double        d0, inpr;
 +    double        lambda, rm, mass, invdt = 0;
 +    gmx_bool      bConverged_all, bConverged = FALSE;
 +    int           niter = 0, g, c, ii, j, m, max_iter = 100;
 +    double        a;
 +    dvec          f;       /* the pull force */
 +    dvec          tmp, tmp3;
 +    t_pull_group *pdyna, *pgrp0, *pgrp1;
 +    t_pull_coord *pcrd;
 +
 +    snew(r_ij,   pull->ncoord);
 +    snew(dr_tot, pull->ncoord);
 +
 +    snew(rnew, pull->ngroup);
 +
 +    /* copy the current unconstrained positions for use in iterations. We
 +       iterate until rinew[i] and rjnew[j] obey the constraints. Then
 +       rinew - pull.x_unc[i] is the correction dr to group i */
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        copy_dvec(pull->group[g].xp, rnew[g]);
 +    }
 +    if (PULL_CYL(pull))
 +    {
 +        /* There is only one pull coordinate and reference group */
 +        copy_dvec(pull->dyna[0].xp, rnew[pull->coord[0].group[0]]);
 +    }
 +
 +    /* Determine the constraint directions from the old positions */
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        get_pull_coord_dr(pull, c, pbc, t, r_ij[c]);
 +        /* Store the difference vector at time t for printing */
 +        copy_dvec(r_ij[c], pull->coord[c].dr);
 +        if (debug)
 +        {
 +            fprintf(debug, "Pull coord %d dr %f %f %f\n",
 +                    c, r_ij[c][XX], r_ij[c][YY], r_ij[c][ZZ]);
 +        }
 +
 +        if (pull->eGeom == epullgDIR || pull->eGeom == epullgDIRPBC)
 +        {
 +            /* Select the component along vec */
 +            a = 0;
 +            for (m = 0; m < DIM; m++)
 +            {
 +                a += pull->coord[c].vec[m]*r_ij[c][m];
 +            }
 +            for (m = 0; m < DIM; m++)
 +            {
 +                r_ij[c][m] = a*pull->coord[c].vec[m];
 +            }
 +        }
 +    }
 +
 +    bConverged_all = FALSE;
 +    while (!bConverged_all && niter < max_iter)
 +    {
 +        bConverged_all = TRUE;
 +
 +        /* loop over all constraints */
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            dvec dr0, dr1;
 +
 +            pcrd  = &pull->coord[c];
 +            pgrp0 = &pull->group[pcrd->group[0]];
 +            pgrp1 = &pull->group[pcrd->group[1]];
 +
 +            /* Get the current difference vector */
 +            low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                                  rnew[pcrd->group[1]],
 +                                  rnew[pcrd->group[0]],
 +                                  -1, unc_ij);
 +
 +            ref = pcrd->init + pcrd->rate*t;
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "Pull coord %d, iteration %d\n", c, niter);
 +            }
 +
 +            rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
 +
 +            switch (pull->eGeom)
 +            {
 +                case epullgDIST:
 +                    if (ref <= 0)
 +                    {
 +                        gmx_fatal(FARGS, "The pull constraint reference distance for group %d is <= 0 (%f)", c, ref);
 +                    }
 +
 +                    {
 +                        double q, c_a, c_b, c_c;
 +
 +                        c_a = diprod(r_ij[c], r_ij[c]);
 +                        c_b = diprod(unc_ij, r_ij[c])*2;
 +                        c_c = diprod(unc_ij, unc_ij) - dsqr(ref);
 +
 +                        if (c_b < 0)
 +                        {
 +                            q      = -0.5*(c_b - sqrt(c_b*c_b - 4*c_a*c_c));
 +                            lambda = -q/c_a;
 +                        }
 +                        else
 +                        {
 +                            q      = -0.5*(c_b + sqrt(c_b*c_b - 4*c_a*c_c));
 +                            lambda = -c_c/q;
 +                        }
 +
 +                        if (debug)
 +                        {
 +                            fprintf(debug,
 +                                    "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
 +                                    c_a, c_b, c_c, lambda);
 +                        }
 +                    }
 +
 +                    /* The position corrections dr due to the constraints */
 +                    dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
 +                    dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
 +                    dr_tot[c] += -lambda*dnorm(r_ij[c]);
 +                    break;
 +                case epullgDIR:
 +                case epullgDIRPBC:
 +                case epullgCYL:
 +                    /* A 1-dimensional constraint along a vector */
 +                    a = 0;
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vec[m] = pcrd->vec[m];
 +                        a     += unc_ij[m]*vec[m];
 +                    }
 +                    /* Select only the component along the vector */
 +                    dsvmul(a, vec, unc_ij);
 +                    lambda = a - ref;
 +                    if (debug)
 +                    {
 +                        fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
 +                    }
 +
 +                    /* The position corrections dr due to the constraints */
 +                    dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
 +                    dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
 +                    dr_tot[c] += -lambda;
 +                    break;
 +            }
 +
 +            /* DEBUG */
 +            if (debug)
 +            {
 +                int g0, g1;
 +
 +                g0 = pcrd->group[0];
 +                g1 = pcrd->group[1];
 +                low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[g1], rnew[g0], -1, tmp);
 +                low_get_pull_coord_dr(pull, pcrd, pbc, t, dr1, dr0, -1, tmp3);
 +                fprintf(debug,
 +                        "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 +                        rnew[g0][0], rnew[g0][1], rnew[g0][2],
 +                        rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
 +                fprintf(debug,
 +                        "Pull ref %8s %8s %8s   %8s %8s %8s d: %8.5f\n",
 +                        "", "", "", "", "", "", ref);
 +                fprintf(debug,
 +                        "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 +                        dr0[0], dr0[1], dr0[2],
 +                        dr1[0], dr1[1], dr1[2],
 +                        dnorm(tmp3));
 +            } /* END DEBUG */
 +
 +            /* Update the COMs with dr */
 +            dvec_inc(rnew[pcrd->group[1]], dr1);
 +            dvec_inc(rnew[pcrd->group[0]], dr0);
 +        }
 +
 +        /* Check if all constraints are fullfilled now */
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            pcrd = &pull->coord[c];
 +
 +            low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                                  rnew[pcrd->group[1]],
 +                                  rnew[pcrd->group[0]],
 +                                  -1, unc_ij);
 +
 +            switch (pull->eGeom)
 +            {
 +                case epullgDIST:
 +                    bConverged = fabs(dnorm(unc_ij) - ref) < pull->constr_tol;
 +                    break;
 +                case epullgDIR:
 +                case epullgDIRPBC:
 +                case epullgCYL:
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vec[m] = pcrd->vec[m];
 +                    }
 +                    inpr = diprod(unc_ij, vec);
 +                    dsvmul(inpr, vec, unc_ij);
 +                    bConverged =
 +                        fabs(diprod(unc_ij, vec) - ref) < pull->constr_tol;
 +                    break;
 +            }
 +
 +            if (!bConverged)
 +            {
 +                if (debug)
 +                {
 +                    fprintf(debug, "NOT CONVERGED YET: Group %d:"
 +                            "d_ref = %f, current d = %f\n",
 +                            g, ref, dnorm(unc_ij));
 +                }
 +
 +                bConverged_all = FALSE;
 +            }
 +        }
 +
 +        niter++;
 +        /* if after all constraints are dealt with and bConverged is still TRUE
 +           we're finished, if not we do another iteration */
 +    }
 +    if (niter > max_iter)
 +    {
 +        gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
 +    }
 +
 +    /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
 +
 +    if (v)
 +    {
 +        invdt = 1/dt;
 +    }
 +
 +    /* update atoms in the groups */
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        const t_pull_group *pgrp;
 +        dvec                dr;
 +
 +        if (PULL_CYL(pull) && g == pull->coord[0].group[0])
 +        {
 +            pgrp = &pull->dyna[0];
 +        }
 +        else
 +        {
 +            pgrp = &pull->group[g];
 +        }
 +
 +        /* get the final constraint displacement dr for group g */
 +        dvec_sub(rnew[g], pgrp->xp, dr);
 +        /* select components of dr */
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dr[m] *= pull->dim[m];
 +        }
 +
 +        /* update the atom positions */
 +        copy_dvec(dr, tmp);
 +        for (j = 0; j < pgrp->nat_loc; j++)
 +        {
 +            ii = pgrp->ind_loc[j];
 +            if (pgrp->weight_loc)
 +            {
 +                dsvmul(pgrp->wscale*pgrp->weight_loc[j], dr, tmp);
 +            }
 +            for (m = 0; m < DIM; m++)
 +            {
 +                x[ii][m] += tmp[m];
 +            }
 +            if (v)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    v[ii][m] += invdt*tmp[m];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* calculate the constraint forces, used for output and virial only */
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd         = &pull->coord[c];
 +        pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->group[0]].invtm + pull->group[pcrd->group[1]].invtm)*dt*dt);
 +
 +        if (vir && bMaster)
 +        {
 +            double f_invr;
 +
 +            /* Add the pull contribution to the virial */
 +            f_invr = pcrd->f_scal/dnorm(r_ij[c]);
 +
 +            for (j = 0; j < DIM; j++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* finished! I hope. Give back some memory */
 +    sfree(r_ij);
 +    sfree(dr_tot);
 +    sfree(rnew);
 +}
 +
 +/* Pulling with a harmonic umbrella potential or constant force */
 +static void do_pull_pot(int ePull,
 +                        t_pull *pull, t_pbc *pbc, double t, real lambda,
 +                        real *V, tensor vir, real *dVdl)
 +{
 +    int           c, j, m;
 +    double        dev, ndr, invdr;
 +    real          k, dkdl;
 +    t_pull_coord *pcrd;
 +
 +    /* loop over the pull coordinates */
 +    *V    = 0;
 +    *dVdl = 0;
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        get_pull_coord_distance(pull, c, pbc, t, pcrd->dr, &dev);
 +
 +        k    = (1.0 - lambda)*pcrd->k + lambda*pcrd->kB;
 +        dkdl = pcrd->kB - pcrd->k;
 +
 +        switch (pull->eGeom)
 +        {
 +            case epullgDIST:
 +                ndr   = dnorm(pcrd->dr);
 +                invdr = 1/ndr;
 +                if (ePull == epullUMBRELLA)
 +                {
 +                    pcrd->f_scal  =       -k*dev;
 +                    *V           += 0.5*   k*dsqr(dev);
 +                    *dVdl        += 0.5*dkdl*dsqr(dev);
 +                }
 +                else
 +                {
 +                    pcrd->f_scal  =   -k;
 +                    *V           +=    k*ndr;
 +                    *dVdl        += dkdl*ndr;
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    pcrd->f[m]    = pcrd->f_scal*pcrd->dr[m]*invdr;
 +                }
 +                break;
 +            case epullgDIR:
 +            case epullgDIRPBC:
 +            case epullgCYL:
 +                if (ePull == epullUMBRELLA)
 +                {
 +                    pcrd->f_scal  =       -k*dev;
 +                    *V           += 0.5*   k*dsqr(dev);
 +                    *dVdl        += 0.5*dkdl*dsqr(dev);
 +                }
 +                else
 +                {
 +                    ndr = 0;
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        ndr += pcrd->vec[m]*pcrd->dr[m];
 +                    }
 +                    pcrd->f_scal  =   -k;
 +                    *V           +=    k*ndr;
 +                    *dVdl        += dkdl*ndr;
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    pcrd->f[m]    = pcrd->f_scal*pcrd->vec[m];
 +                }
 +                break;
 +        }
 +
 +        if (vir)
 +        {
 +            /* Add the pull contribution to the virial */
 +            for (j = 0; j < DIM; j++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    vir[j][m] -= 0.5*pcrd->f[j]*pcrd->dr[m];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +real pull_potential(int ePull, t_pull *pull, t_mdatoms *md, t_pbc *pbc,
 +                    t_commrec *cr, double t, real lambda,
 +                    rvec *x, rvec *f, tensor vir, real *dvdlambda)
 +{
 +    real V, dVdl;
 +
 +    pull_calc_coms(cr, pull, md, pbc, t, x, NULL);
 +
 +    do_pull_pot(ePull, pull, pbc, t, lambda,
 +                &V, pull->bVirial && MASTER(cr) ? vir : NULL, &dVdl);
 +
 +    /* Distribute forces over pulled groups */
 +    apply_forces(pull, md, f);
 +
 +    if (MASTER(cr))
 +    {
 +        *dvdlambda += dVdl;
 +    }
 +
 +    return (MASTER(cr) ? V : 0.0);
 +}
 +
 +void pull_constraint(t_pull *pull, t_mdatoms *md, t_pbc *pbc,
 +                     t_commrec *cr, double dt, double t,
 +                     rvec *x, rvec *xp, rvec *v, tensor vir)
 +{
 +    pull_calc_coms(cr, pull, md, pbc, t, x, xp);
 +
 +    do_constraint(pull, pbc, xp, v, pull->bVirial && MASTER(cr), vir, dt, t);
 +}
 +
 +static void make_local_pull_group(gmx_ga2la_t ga2la,
 +                                  t_pull_group *pg, int start, int end)
 +{
 +    int i, ii;
 +
 +    pg->nat_loc = 0;
 +    for (i = 0; i < pg->nat; i++)
 +    {
 +        ii = pg->ind[i];
 +        if (ga2la)
 +        {
 +            if (!ga2la_get_home(ga2la, ii, &ii))
 +            {
 +                ii = -1;
 +            }
 +        }
 +        if (ii >= start && ii < end)
 +        {
 +            /* This is a home atom, add it to the local pull group */
 +            if (pg->nat_loc >= pg->nalloc_loc)
 +            {
 +                pg->nalloc_loc = over_alloc_dd(pg->nat_loc+1);
 +                srenew(pg->ind_loc, pg->nalloc_loc);
 +                if (pg->epgrppbc == epgrppbcCOS || pg->weight)
 +                {
 +                    srenew(pg->weight_loc, pg->nalloc_loc);
 +                }
 +            }
 +            pg->ind_loc[pg->nat_loc] = ii;
 +            if (pg->weight)
 +            {
 +                pg->weight_loc[pg->nat_loc] = pg->weight[i];
 +            }
 +            pg->nat_loc++;
 +        }
 +    }
 +}
 +
 +void dd_make_local_pull_groups(gmx_domdec_t *dd, t_pull *pull, t_mdatoms *md)
 +{
 +    gmx_ga2la_t ga2la;
 +    int         g;
 +
 +    if (dd)
 +    {
 +        ga2la = dd->ga2la;
 +    }
 +    else
 +    {
 +        ga2la = NULL;
 +    }
 +
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        make_local_pull_group(ga2la, &pull->group[g],
 +                              0, md->homenr);
 +    }
 +}
 +
 +static void init_pull_group_index(FILE *fplog, t_commrec *cr,
 +                                  int g, t_pull_group *pg, ivec pulldims,
 +                                  gmx_mtop_t *mtop, t_inputrec *ir, real lambda)
 +{
 +    int                   i, ii, d, nfrozen, ndim;
 +    real                  m, w, mbd;
 +    double                tmass, wmass, wwmass;
 +    gmx_groups_t         *groups;
 +    gmx_mtop_atomlookup_t alook;
 +    t_atom               *atom;
 +
 +    if (EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +    {
 +        /* There are no masses in the integrator.
 +         * But we still want to have the correct mass-weighted COMs.
 +         * So we store the real masses in the weights.
 +         * We do not set nweight, so these weights do not end up in the tpx file.
 +         */
 +        if (pg->nweight == 0)
 +        {
 +            snew(pg->weight, pg->nat);
 +        }
 +    }
 +
 +    if (cr && PAR(cr))
 +    {
 +        pg->nat_loc    = 0;
 +        pg->nalloc_loc = 0;
 +        pg->ind_loc    = NULL;
 +        pg->weight_loc = NULL;
 +    }
 +    else
 +    {
 +        pg->nat_loc = pg->nat;
 +        pg->ind_loc = pg->ind;
 +        if (pg->epgrppbc == epgrppbcCOS)
 +        {
 +            snew(pg->weight_loc, pg->nat);
 +        }
 +        else
 +        {
 +            pg->weight_loc = pg->weight;
 +        }
 +    }
 +
 +    groups = &mtop->groups;
 +
 +    alook = gmx_mtop_atomlookup_init(mtop);
 +
 +    nfrozen = 0;
 +    tmass   = 0;
 +    wmass   = 0;
 +    wwmass  = 0;
 +    for (i = 0; i < pg->nat; i++)
 +    {
 +        ii = pg->ind[i];
 +        gmx_mtop_atomnr_to_atom(alook, ii, &atom);
 +        if (ir->opts.nFreeze)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (pulldims[d] && ir->opts.nFreeze[ggrpnr(groups, egcFREEZE, ii)][d])
 +                {
 +                    nfrozen++;
 +                }
 +            }
 +        }
 +        if (ir->efep == efepNO)
 +        {
 +            m = atom->m;
 +        }
 +        else
 +        {
 +            m = (1 - lambda)*atom->m + lambda*atom->mB;
 +        }
 +        if (pg->nweight > 0)
 +        {
 +            w = pg->weight[i];
 +        }
 +        else
 +        {
 +            w = 1;
 +        }
 +        if (EI_ENERGY_MINIMIZATION(ir->eI))
 +        {
 +            /* Move the mass to the weight */
 +            w            *= m;
 +            m             = 1;
 +            pg->weight[i] = w;
 +        }
 +        else if (ir->eI == eiBD)
 +        {
 +            if (ir->bd_fric)
 +            {
 +                mbd = ir->bd_fric*ir->delta_t;
 +            }
 +            else
 +            {
 +                if (groups->grpnr[egcTC] == NULL)
 +                {
 +                    mbd = ir->delta_t/ir->opts.tau_t[0];
 +                }
 +                else
 +                {
 +                    mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
 +                }
 +            }
 +            w            *= m/mbd;
 +            m             = mbd;
 +            pg->weight[i] = w;
 +        }
 +        tmass  += m;
 +        wmass  += m*w;
 +        wwmass += m*w*w;
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +
 +    if (wmass == 0)
 +    {
 +        gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
 +                  pg->weight ? " weighted" : "", g);
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog,
 +                "Pull group %d: %5d atoms, mass %9.3f", g, pg->nat, tmass);
 +        if (pg->weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +        {
 +            fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
 +        }
 +        if (pg->epgrppbc == epgrppbcCOS)
 +        {
 +            fprintf(fplog, ", cosine weighting will be used");
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    if (nfrozen == 0)
 +    {
 +        /* A value > 0 signals not frozen, it is updated later */
 +        pg->invtm  = 1.0;
 +    }
 +    else
 +    {
 +        ndim = 0;
 +        for (d = 0; d < DIM; d++)
 +        {
 +            ndim += pulldims[d]*pg->nat;
 +        }
 +        if (fplog && nfrozen > 0 && nfrozen < ndim)
 +        {
 +            fprintf(fplog,
 +                    "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
 +                    "         that are subject to pulling are frozen.\n"
 +                    "         For pulling the whole group will be frozen.\n\n",
 +                    g);
 +        }
 +        pg->invtm  = 0.0;
 +        pg->wscale = 1.0;
 +    }
 +}
 +
 +void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
 +               gmx_mtop_t *mtop, t_commrec *cr, const output_env_t oenv, real lambda,
 +               gmx_bool bOutFile, unsigned long Flags)
 +{
 +    t_pull       *pull;
 +    t_pull_group *pgrp;
 +    int           c, g, start = 0, end = 0, m;
 +
 +    pull = ir->pull;
 +
 +    pull->ePBC = ir->ePBC;
 +    switch (pull->ePBC)
 +    {
 +        case epbcNONE: pull->npbcdim = 0; break;
 +        case epbcXY:   pull->npbcdim = 2; break;
 +        default:       pull->npbcdim = 3; break;
 +    }
 +
 +    if (fplog)
 +    {
 +        gmx_bool bAbs, bCos;
 +
 +        bAbs = FALSE;
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (pull->group[pull->coord[c].group[0]].nat == 0 ||
 +                pull->group[pull->coord[c].group[1]].nat == 0)
 +            {
 +                bAbs = TRUE;
 +            }
 +        }
 +
 +        fprintf(fplog, "\nWill apply %s COM pulling in geometry '%s'\n",
 +                EPULLTYPE(ir->ePull), EPULLGEOM(pull->eGeom));
 +        fprintf(fplog, "with %d pull coordinate%s and %d group%s\n",
 +                pull->ncoord, pull->ncoord == 1 ? "" : "s",
 +                pull->ngroup, pull->ngroup == 1 ? "" : "s");
 +        if (bAbs)
 +        {
 +            fprintf(fplog, "with an absolute reference\n");
 +        }
 +        bCos = FALSE;
 +        for (g = 0; g < pull->ngroup; g++)
 +        {
 +            if (pull->group[g].nat > 1 &&
 +                pull->group[g].pbcatom < 0)
 +            {
 +                /* We are using cosine weighting */
 +                fprintf(fplog, "Cosine weighting is used for group %d\n", g);
 +                bCos = TRUE;
 +            }
 +        }
 +        if (bCos)
 +        {
 +            please_cite(fplog, "Engin2010");
 +        }
 +    }
 +
 +    /* We always add the virial contribution,
 +     * except for geometry = direction_periodic where this is impossible.
 +     */
 +    pull->bVirial = (pull->eGeom != epullgDIRPBC);
 +    if (getenv("GMX_NO_PULLVIR") != NULL)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Found env. var., will not add the virial contribution of the COM pull forces\n");
 +        }
 +        pull->bVirial = FALSE;
 +    }
 +
 +    pull->rbuf     = NULL;
 +    pull->dbuf     = NULL;
 +    pull->dbuf_cyl = NULL;
 +    pull->bRefAt   = FALSE;
 +    pull->cosdim   = -1;
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        pgrp           = &pull->group[g];
 +        pgrp->epgrppbc = epgrppbcNONE;
 +        if (pgrp->nat > 0)
 +        {
 +            /* Determine if we need to take PBC into account for calculating
 +             * the COM's of the pull groups.
 +             */
 +            for (m = 0; m < pull->npbcdim; m++)
 +            {
 +                if (pull->dim[m] && pgrp->nat > 1)
 +                {
 +                    if (pgrp->pbcatom >= 0)
 +                    {
 +                        pgrp->epgrppbc = epgrppbcREFAT;
 +                        pull->bRefAt   = TRUE;
 +                    }
 +                    else
 +                    {
 +                        if (pgrp->weight)
 +                        {
 +                            gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
 +                        }
 +                        pgrp->epgrppbc = epgrppbcCOS;
 +                        if (pull->cosdim >= 0 && pull->cosdim != m)
 +                        {
 +                            gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull_dim)");
 +                        }
 +                        pull->cosdim = m;
 +                    }
 +                }
 +            }
 +            /* Set the indices */
 +            init_pull_group_index(fplog, cr, g, pgrp, pull->dim, mtop, ir, lambda);
 +            if (PULL_CYL(pull) && pgrp->invtm == 0)
 +            {
 +                gmx_fatal(FARGS, "Can not have frozen atoms in a cylinder pull group");
 +            }
 +        }
 +        else
 +        {
 +            /* Absolute reference, set the inverse mass to zero */
 +            pgrp->invtm  = 0;
 +            pgrp->wscale = 1;
 +        }
 +    }
 +
 +    /* if we use dynamic reference groups, do some initialising for them */
 +    if (PULL_CYL(pull))
 +    {
 +        if (ir->ePull == epullCONSTRAINT && pull->ncoord > 1)
 +        {
 +            /* We can't easily update the single reference group with multiple
 +             * constraints. This would require recalculating COMs.
 +             */
 +            gmx_fatal(FARGS, "Constraint COM pulling supports only one coordinate with geometry=cylinder, you can use umbrella pulling with multiple coordinates");
 +        }
 +
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (pull->group[pull->coord[c].group[0]].nat == 0)
 +            {
 +                gmx_fatal(FARGS, "Dynamic reference groups are not supported when using absolute reference!\n");
 +            }
 +        }
 +
 +        snew(pull->dyna, pull->ncoord);
 +    }
 +
 +    /* Only do I/O when we are doing dynamics and if we are the MASTER */
 +    pull->out_x = NULL;
 +    pull->out_f = NULL;
 +    if (bOutFile)
 +    {
 +        if (pull->nstxout > 0)
 +        {
 +            pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv, TRUE, Flags);
 +        }
 +        if (pull->nstfout > 0)
 +        {
 +            pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
 +                                        FALSE, Flags);
 +        }
 +    }
 +}
 +
 +void finish_pull(t_pull *pull)
 +{
 +    if (pull->out_x)
 +    {
 +        gmx_fio_fclose(pull->out_x);
 +    }
 +    if (pull->out_f)
 +    {
 +        gmx_fio_fclose(pull->out_f);
 +    }
 +}
diff --combined src/programs/mdrun/md.c
index e49e19b2f2cccaba65e69df6226a1b4ab8526749,0000000000000000000000000000000000000000..018daf5d0efda975b0ea601a45306acf48843f08
mode 100644,000000..100644
--- /dev/null
@@@ -1,1993 -1,0 +1,2009 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "sysstuff.h"
 +#include "vec.h"
 +#include "vcm.h"
 +#include "mdebin.h"
 +#include "nrnb.h"
 +#include "calcmu.h"
 +#include "index.h"
 +#include "vsite.h"
 +#include "update.h"
 +#include "ns.h"
 +#include "mdrun.h"
 +#include "md_support.h"
 +#include "md_logging.h"
 +#include "network.h"
 +#include "xvgr.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "force.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "pme.h"
 +#include "mdatoms.h"
 +#include "repl_ex.h"
 +#include "deform.h"
 +#include "qmmm.h"
 +#include "domdec.h"
 +#include "domdec_network.h"
 +#include "gromacs/gmxlib/topsort.h"
 +#include "coulomb.h"
 +#include "constr.h"
 +#include "shellfc.h"
 +#include "gromacs/gmxpreprocess/compute_io.h"
 +#include "checkpoint.h"
 +#include "mtop_util.h"
 +#include "sighandler.h"
 +#include "txtdump.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "pme_loadbal.h"
 +#include "bondf.h"
 +#include "membed.h"
 +#include "types/nlistheuristics.h"
 +#include "types/iteratedconstraints.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/trajectory_writing.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/fileio/xtcio.h"
 +#include "gromacs/timing/wallcycle.h"
 +#include "gromacs/timing/walltime_accounting.h"
 +#include "gromacs/pulling/pull.h"
 +#include "gromacs/swap/swapcoords.h"
 +#include "gromacs/imd/imd.h"
 +
 +#ifdef GMX_FAHCORE
 +#include "corewrap.h"
 +#endif
 +
 +static void reset_all_counters(FILE *fplog, t_commrec *cr,
 +                               gmx_int64_t step,
 +                               gmx_int64_t *step_rel, t_inputrec *ir,
 +                               gmx_wallcycle_t wcycle, t_nrnb *nrnb,
 +                               gmx_walltime_accounting_t walltime_accounting,
 +                               nbnxn_cuda_ptr_t cu_nbv)
 +{
 +    char sbuf[STEPSTRSIZE];
 +
 +    /* Reset all the counters related to performance over the run */
 +    md_print_warn(cr, fplog, "step %s: resetting all time and cycle counters\n",
 +                  gmx_step_str(step, sbuf));
 +
 +    if (cu_nbv)
 +    {
 +        nbnxn_cuda_reset_timings(cu_nbv);
 +    }
 +
 +    wallcycle_stop(wcycle, ewcRUN);
 +    wallcycle_reset_all(wcycle);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        reset_dd_statistics_counters(cr->dd);
 +    }
 +    init_nrnb(nrnb);
 +    ir->init_step += *step_rel;
 +    ir->nsteps    -= *step_rel;
 +    *step_rel      = 0;
 +    wallcycle_start(wcycle, ewcRUN);
 +    walltime_accounting_start(walltime_accounting);
 +    print_date_and_time(fplog, cr->nodeid, "Restarted time", gmx_gettime());
 +}
 +
 +double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
 +             const output_env_t oenv, gmx_bool bVerbose, gmx_bool bCompact,
 +             int nstglobalcomm,
 +             gmx_vsite_t *vsite, gmx_constr_t constr,
 +             int stepout, t_inputrec *ir,
 +             gmx_mtop_t *top_global,
 +             t_fcdata *fcd,
 +             t_state *state_global,
 +             t_mdatoms *mdatoms,
 +             t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +             gmx_edsam_t ed, t_forcerec *fr,
 +             int repl_ex_nst, int repl_ex_nex, int repl_ex_seed, gmx_membed_t membed,
 +             real cpt_period, real max_hours,
 +             const char gmx_unused *deviceOptions,
 +             int imdport,
 +             unsigned long Flags,
 +             gmx_walltime_accounting_t walltime_accounting)
 +{
 +    gmx_mdoutf_t    outf = NULL;
 +    gmx_int64_t     step, step_rel;
 +    double          elapsed_time;
 +    double          t, t0, lam0[efptNR];
 +    gmx_bool        bGStatEveryStep, bGStat, bCalcVir, bCalcEner;
 +    gmx_bool        bNS, bNStList, bSimAnn, bStopCM, bRerunMD, bNotLastFrame = FALSE,
 +                    bFirstStep, bStateFromCP, bStateFromTPX, bInitStep, bLastStep,
 +                    bBornRadii, bStartingFromCpt;
 +    gmx_bool          bDoDHDL = FALSE, bDoFEP = FALSE, bDoExpanded = FALSE;
 +    gmx_bool          do_ene, do_log, do_verbose, bRerunWarnNoV = TRUE,
 +                      bForceUpdate = FALSE, bCPT;
 +    gmx_bool          bMasterState;
 +    int               force_flags, cglo_flags;
 +    tensor            force_vir, shake_vir, total_vir, tmp_vir, pres;
 +    int               i, m;
 +    t_trxstatus      *status;
 +    rvec              mu_tot;
 +    t_vcm            *vcm;
 +    t_state          *bufstate = NULL;
 +    matrix           *scale_tot, pcoupl_mu, M, ebox;
 +    gmx_nlheur_t      nlh;
 +    t_trxframe        rerun_fr;
 +    gmx_repl_ex_t     repl_ex = NULL;
 +    int               nchkpt  = 1;
 +    gmx_localtop_t   *top;
 +    t_mdebin         *mdebin   = NULL;
 +    t_state          *state    = NULL;
 +    rvec             *f_global = NULL;
 +    gmx_enerdata_t   *enerd;
 +    rvec             *f = NULL;
 +    gmx_global_stat_t gstat;
 +    gmx_update_t      upd   = NULL;
 +    t_graph          *graph = NULL;
 +    globsig_t         gs;
 +    gmx_groups_t     *groups;
 +    gmx_ekindata_t   *ekind, *ekind_save;
 +    gmx_shellfc_t     shellfc;
 +    int               count, nconverged = 0;
 +    real              timestep   = 0;
 +    double            tcount     = 0;
 +    gmx_bool          bConverged = TRUE, bOK, bSumEkinhOld, bExchanged, bNeedRepartition;
 +    gmx_bool          bAppend;
 +    gmx_bool          bResetCountersHalfMaxH = FALSE;
 +    gmx_bool          bVV, bIterativeCase, bFirstIterate, bTemp, bPres, bTrotter;
 +    gmx_bool          bUpdateDoLR;
 +    real              dvdl_constr;
 +    rvec             *cbuf = NULL;
 +    matrix            lastbox;
 +    real              veta_save, scalevir, tracevir;
 +    real              vetanew = 0;
 +    int               lamnew  = 0;
 +    /* for FEP */
 +    int               nstfep;
 +    double            cycles;
 +    real              saved_conserved_quantity = 0;
 +    real              last_ekin                = 0;
 +    int               iter_i;
 +    t_extmass         MassQ;
 +    int             **trotter_seq;
 +    char              sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE];
 +    int               handled_stop_condition = gmx_stop_cond_none; /* compare to get_stop_condition*/
 +    gmx_iterate_t     iterate;
 +    gmx_int64_t       multisim_nsteps = -1;                        /* number of steps to do  before first multisim
 +                                                                          simulation stops. If equal to zero, don't
 +                                                                          communicate any more between multisims.*/
 +    /* PME load balancing data for GPU kernels */
 +    pme_load_balancing_t pme_loadbal = NULL;
 +    double               cycles_pmes;
 +    gmx_bool             bPMETuneTry = FALSE, bPMETuneRunning = FALSE;
 +
 +    /* Interactive MD */
 +    gmx_bool          bIMDstep = FALSE;
 +
 +#ifdef GMX_FAHCORE
 +    /* Temporary addition for FAHCORE checkpointing */
 +    int chkpt_ret;
 +#endif
 +
 +    /* Check for special mdrun options */
 +    bRerunMD = (Flags & MD_RERUN);
 +    bAppend  = (Flags & MD_APPENDFILES);
 +    if (Flags & MD_RESETCOUNTERSHALFWAY)
 +    {
 +        if (ir->nsteps > 0)
 +        {
 +            /* Signal to reset the counters half the simulation steps. */
 +            wcycle_set_reset_counters(wcycle, ir->nsteps/2);
 +        }
 +        /* Signal to reset the counters halfway the simulation time. */
 +        bResetCountersHalfMaxH = (max_hours > 0);
 +    }
 +
 +    /* md-vv uses averaged full step velocities for T-control
 +       md-vv-avek uses averaged half step velocities for T-control (but full step ekin for P control)
 +       md uses averaged half step kinetic energies to determine temperature unless defined otherwise by GMX_EKIN_AVE_VEL; */
 +    bVV = EI_VV(ir->eI);
 +    if (bVV) /* to store the initial velocities while computing virial */
 +    {
 +        snew(cbuf, top_global->natoms);
 +    }
 +    /* all the iteratative cases - only if there are constraints */
 +    bIterativeCase = ((IR_NPH_TROTTER(ir) || IR_NPT_TROTTER(ir)) && (constr) && (!bRerunMD));
 +    gmx_iterate_init(&iterate, FALSE); /* The default value of iterate->bIterationActive is set to
 +                                          false in this step.  The correct value, true or false,
 +                                          is set at each step, as it depends on the frequency of temperature
 +                                          and pressure control.*/
 +    bTrotter = (bVV && (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir) || IR_NVT_TROTTER(ir)));
 +
 +    if (bRerunMD)
 +    {
 +        /* Since we don't know if the frames read are related in any way,
 +         * rebuild the neighborlist at every step.
 +         */
 +        ir->nstlist       = 1;
 +        ir->nstcalcenergy = 1;
 +        nstglobalcomm     = 1;
 +    }
 +
 +    check_ir_old_tpx_versions(cr, fplog, ir, top_global);
 +
 +    nstglobalcomm   = check_nstglobalcomm(fplog, cr, nstglobalcomm, ir);
 +    bGStatEveryStep = (nstglobalcomm == 1);
 +
 +    if (!bGStatEveryStep && ir->nstlist == -1 && fplog != NULL)
 +    {
 +        fprintf(fplog,
 +                "To reduce the energy communication with nstlist = -1\n"
 +                "the neighbor list validity should not be checked at every step,\n"
 +                "this means that exact integration is not guaranteed.\n"
 +                "The neighbor list validity is checked after:\n"
 +                "  <n.list life time> - 2*std.dev.(n.list life time)  steps.\n"
 +                "In most cases this will result in exact integration.\n"
 +                "This reduces the energy communication by a factor of 2 to 3.\n"
 +                "If you want less energy communication, set nstlist > 3.\n\n");
 +    }
 +
 +    if (bRerunMD)
 +    {
 +        ir->nstxout_compressed = 0;
 +    }
 +    groups = &top_global->groups;
 +
 +    /* Initial values */
 +    init_md(fplog, cr, ir, oenv, &t, &t0, state_global->lambda,
 +            &(state_global->fep_state), lam0,
 +            nrnb, top_global, &upd,
 +            nfile, fnm, &outf, &mdebin,
 +            force_vir, shake_vir, mu_tot, &bSimAnn, &vcm, Flags);
 +
 +    clear_mat(total_vir);
 +    clear_mat(pres);
 +    /* Energy terms and groups */
 +    snew(enerd, 1);
 +    init_enerdata(top_global->groups.grps[egcENER].nr, ir->fepvals->n_lambda,
 +                  enerd);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        f = NULL;
 +    }
 +    else
 +    {
 +        snew(f, top_global->natoms);
 +    }
 +
 +    /* Kinetic energy data */
 +    snew(ekind, 1);
 +    init_ekindata(fplog, top_global, &(ir->opts), ekind);
 +    /* needed for iteration of constraints */
 +    snew(ekind_save, 1);
 +    init_ekindata(fplog, top_global, &(ir->opts), ekind_save);
 +    /* Copy the cos acceleration to the groups struct */
 +    ekind->cosacc.cos_accel = ir->cos_accel;
 +
 +    gstat = global_stat_init(ir);
 +    debug_gmx();
 +
 +    /* Check for polarizable models and flexible constraints */
 +    shellfc = init_shell_flexcon(fplog, fr->cutoff_scheme == ecutsVERLET,
 +                                 top_global, n_flexible_constraints(constr),
 +                                 (ir->bContinuation ||
 +                                  (DOMAINDECOMP(cr) && !MASTER(cr))) ?
 +                                 NULL : state_global->x);
 +
++    if (shellfc && ir->eI == eiNM)
++    {
++        /* Currently shells don't work with Normal Modes */
++        gmx_fatal(FARGS, "Normal Mode analysis is not supported with shells.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
++    }
++
++    if (vsite && ir->eI == eiNM)
++    {
++        /* Currently virtual sites don't work with Normal Modes */
++        gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
++    }
++
 +    if (DEFORM(*ir))
 +    {
 +        tMPI_Thread_mutex_lock(&deform_init_box_mutex);
 +        set_deform_reference_box(upd,
 +                                 deform_init_init_step_tpx,
 +                                 deform_init_box_tpx);
 +        tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
 +    }
 +
 +    {
 +        double io = compute_io(ir, top_global->natoms, groups, mdebin->ebin->nener, 1);
 +        if ((io > 2000) && MASTER(cr))
 +        {
 +            fprintf(stderr,
 +                    "\nWARNING: This run will generate roughly %.0f Mb of data\n\n",
 +                    io);
 +        }
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        top = dd_init_local_top(top_global);
 +
 +        snew(state, 1);
 +        dd_init_local_state(cr->dd, state_global, state);
 +
 +        if (DDMASTER(cr->dd) && ir->nstfout)
 +        {
 +            snew(f_global, state_global->natoms);
 +        }
 +    }
 +    else
 +    {
 +        top = gmx_mtop_generate_local_top(top_global, ir);
 +
 +        forcerec_set_excl_load(fr, top);
 +
 +        state    = serial_init_local_state(state_global);
 +        f_global = f;
 +
 +        atoms2md(top_global, ir, 0, NULL, top_global->natoms, mdatoms);
 +
 +        if (vsite)
 +        {
 +            set_vsite_top(vsite, top, mdatoms, cr);
 +        }
 +
 +        if (ir->ePBC != epbcNONE && !fr->bMolPBC)
 +        {
 +            graph = mk_graph(fplog, &(top->idef), 0, top_global->natoms, FALSE, FALSE);
 +        }
 +
 +        if (shellfc)
 +        {
 +            make_local_shells(cr, mdatoms, shellfc);
 +        }
 +
 +        setup_bonded_threading(fr, &top->idef);
 +    }
 +
 +    /* Set up interactive MD (IMD) */
 +    init_IMD(ir, cr, top_global, fplog, ir->nstcalcenergy, state_global->x,
 +             nfile, fnm, oenv, imdport, Flags);
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        /* Distribute the charge groups over the nodes from the master node */
 +        dd_partition_system(fplog, ir->init_step, cr, TRUE, 1,
 +                            state_global, top_global, ir,
 +                            state, &f, mdatoms, top, fr,
 +                            vsite, shellfc, constr,
 +                            nrnb, wcycle, FALSE);
 +
 +    }
 +
 +    update_mdatoms(mdatoms, state->lambda[efptMASS]);
 +
 +    if (opt2bSet("-cpi", nfile, fnm))
 +    {
 +        bStateFromCP = gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr);
 +    }
 +    else
 +    {
 +        bStateFromCP = FALSE;
 +    }
 +
 +    if (ir->bExpanded)
 +    {
 +        init_expanded_ensemble(bStateFromCP, ir, &state->dfhist);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        if (bStateFromCP)
 +        {
 +            /* Update mdebin with energy history if appending to output files */
 +            if (Flags & MD_APPENDFILES)
 +            {
 +                restore_energyhistory_from_state(mdebin, &state_global->enerhist);
 +            }
 +            else
 +            {
 +                /* We might have read an energy history from checkpoint,
 +                 * free the allocated memory and reset the counts.
 +                 */
 +                done_energyhistory(&state_global->enerhist);
 +                init_energyhistory(&state_global->enerhist);
 +            }
 +        }
 +        /* Set the initial energy history in state by updating once */
 +        update_energyhistory(&state_global->enerhist, mdebin);
 +    }
 +
 +    /* Initialize constraints */
 +    if (constr && !DOMAINDECOMP(cr))
 +    {
 +        set_constraints(constr, top, ir, mdatoms, cr);
 +    }
 +
 +    if (repl_ex_nst > 0)
 +    {
 +        /* We need to be sure replica exchange can only occur
 +         * when the energies are current */
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "repl_ex_nst", &repl_ex_nst);
 +        /* This check needs to happen before inter-simulation
 +         * signals are initialized, too */
 +    }
 +    if (repl_ex_nst > 0 && MASTER(cr))
 +    {
 +        repl_ex = init_replica_exchange(fplog, cr->ms, state_global, ir,
 +                                        repl_ex_nst, repl_ex_nex, repl_ex_seed);
 +    }
 +
 +    /* PME tuning is only supported with GPUs or PME nodes and not with rerun.
 +     * PME tuning is not supported with PME only for LJ and not for Coulomb.
 +     */
 +    if ((Flags & MD_TUNEPME) &&
 +        EEL_PME(fr->eeltype) &&
 +        ( (fr->cutoff_scheme == ecutsVERLET && fr->nbv->bUseGPU) || !(cr->duty & DUTY_PME)) &&
 +        !bRerunMD)
 +    {
 +        pme_loadbal_init(&pme_loadbal, ir, state->box, fr->ic, fr->pmedata);
 +        cycles_pmes = 0;
 +        if (cr->duty & DUTY_PME)
 +        {
 +            /* Start tuning right away, as we can't measure the load */
 +            bPMETuneRunning = TRUE;
 +        }
 +        else
 +        {
 +            /* Separate PME nodes, we can measure the PP/PME load balance */
 +            bPMETuneTry = TRUE;
 +        }
 +    }
 +
 +    if (!ir->bContinuation && !bRerunMD)
 +    {
 +        if (mdatoms->cFREEZE && (state->flags & (1<<estV)))
 +        {
 +            /* Set the velocities of frozen particles to zero */
 +            for (i = 0; i < mdatoms->homenr; i++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (ir->opts.nFreeze[mdatoms->cFREEZE[i]][m])
 +                    {
 +                        state->v[i][m] = 0;
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (constr)
 +        {
 +            /* Constrain the initial coordinates and velocities */
 +            do_constrain_first(fplog, constr, ir, mdatoms, state,
 +                               cr, nrnb, fr, top);
 +        }
 +        if (vsite)
 +        {
 +            /* Construct the virtual sites for the initial configuration */
 +            construct_vsites(vsite, state->x, ir->delta_t, NULL,
 +                             top->idef.iparams, top->idef.il,
 +                             fr->ePBC, fr->bMolPBC, cr, state->box);
 +        }
 +    }
 +
 +    debug_gmx();
 +
 +    /* set free energy calculation frequency as the minimum
 +       greatest common denominator of nstdhdl, nstexpanded, and repl_ex_nst*/
 +    nstfep = ir->fepvals->nstdhdl;
 +    if (ir->bExpanded)
 +    {
 +        nstfep = gmx_greatest_common_divisor(ir->fepvals->nstdhdl, nstfep);
 +    }
 +    if (repl_ex_nst > 0)
 +    {
 +        nstfep = gmx_greatest_common_divisor(repl_ex_nst, nstfep);
 +    }
 +
 +    /* I'm assuming we need global communication the first time! MRS */
 +    cglo_flags = (CGLO_TEMPERATURE | CGLO_GSTAT
 +                  | ((ir->comm_mode != ecmNO) ? CGLO_STOPCM : 0)
 +                  | (bVV ? CGLO_PRESSURE : 0)
 +                  | (bVV ? CGLO_CONSTRAINT : 0)
 +                  | (bRerunMD ? CGLO_RERUNMD : 0)
 +                  | ((Flags & MD_READ_EKIN) ? CGLO_READEKIN : 0));
 +
 +    bSumEkinhOld = FALSE;
 +    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                    NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                    constr, NULL, FALSE, state->box,
 +                    top_global, &bSumEkinhOld, cglo_flags);
 +    if (ir->eI == eiVVAK)
 +    {
 +        /* a second call to get the half step temperature initialized as well */
 +        /* we do the same call as above, but turn the pressure off -- internally to
 +           compute_globals, this is recognized as a velocity verlet half-step
 +           kinetic energy calculation.  This minimized excess variables, but
 +           perhaps loses some logic?*/
 +
 +        compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                        NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                        constr, NULL, FALSE, state->box,
 +                        top_global, &bSumEkinhOld,
 +                        cglo_flags &~(CGLO_STOPCM | CGLO_PRESSURE));
 +    }
 +
 +    /* Calculate the initial half step temperature, and save the ekinh_old */
 +    if (!(Flags & MD_STARTFROMCPT))
 +    {
 +        for (i = 0; (i < ir->opts.ngtc); i++)
 +        {
 +            copy_mat(ekind->tcstat[i].ekinh, ekind->tcstat[i].ekinh_old);
 +        }
 +    }
 +    if (ir->eI != eiVV)
 +    {
 +        enerd->term[F_TEMP] *= 2; /* result of averages being done over previous and current step,
 +                                     and there is no previous step */
 +    }
 +
 +    /* if using an iterative algorithm, we need to create a working directory for the state. */
 +    if (bIterativeCase)
 +    {
 +        bufstate = init_bufstate(state);
 +    }
 +
 +    /* need to make an initiation call to get the Trotter variables set, as well as other constants for non-trotter
 +       temperature control */
 +    trotter_seq = init_npt_vars(ir, state, &MassQ, bTrotter);
 +
 +    if (MASTER(cr))
 +    {
 +        if (constr && !ir->bContinuation && ir->eConstrAlg == econtLINCS)
 +        {
 +            fprintf(fplog,
 +                    "RMS relative constraint deviation after constraining: %.2e\n",
 +                    constr_rmsd(constr, FALSE));
 +        }
 +        if (EI_STATE_VELOCITY(ir->eI))
 +        {
 +            fprintf(fplog, "Initial temperature: %g K\n", enerd->term[F_TEMP]);
 +        }
 +        if (bRerunMD)
 +        {
 +            fprintf(stderr, "starting md rerun '%s', reading coordinates from"
 +                    " input trajectory '%s'\n\n",
 +                    *(top_global->name), opt2fn("-rerun", nfile, fnm));
 +            if (bVerbose)
 +            {
 +                fprintf(stderr, "Calculated time to finish depends on nsteps from "
 +                        "run input file,\nwhich may not correspond to the time "
 +                        "needed to process input trajectory.\n\n");
 +            }
 +        }
 +        else
 +        {
 +            char tbuf[20];
 +            fprintf(stderr, "starting mdrun '%s'\n",
 +                    *(top_global->name));
 +            if (ir->nsteps >= 0)
 +            {
 +                sprintf(tbuf, "%8.1f", (ir->init_step+ir->nsteps)*ir->delta_t);
 +            }
 +            else
 +            {
 +                sprintf(tbuf, "%s", "infinite");
 +            }
 +            if (ir->init_step > 0)
 +            {
 +                fprintf(stderr, "%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
 +                        gmx_step_str(ir->init_step+ir->nsteps, sbuf), tbuf,
 +                        gmx_step_str(ir->init_step, sbuf2),
 +                        ir->init_step*ir->delta_t);
 +            }
 +            else
 +            {
 +                fprintf(stderr, "%s steps, %s ps.\n",
 +                        gmx_step_str(ir->nsteps, sbuf), tbuf);
 +            }
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    walltime_accounting_start(walltime_accounting);
 +    wallcycle_start(wcycle, ewcRUN);
 +    print_start(fplog, cr, walltime_accounting, "mdrun");
 +
 +    /* safest point to do file checkpointing is here.  More general point would be immediately before integrator call */
 +#ifdef GMX_FAHCORE
 +    chkpt_ret = fcCheckPointParallel( cr->nodeid,
 +                                      NULL, 0);
 +    if (chkpt_ret == 0)
 +    {
 +        gmx_fatal( 3, __FILE__, __LINE__, "Checkpoint error on step %d\n", 0 );
 +    }
 +#endif
 +
 +    debug_gmx();
 +    /***********************************************************
 +     *
 +     *             Loop over MD steps
 +     *
 +     ************************************************************/
 +
 +    /* if rerunMD then read coordinates and velocities from input trajectory */
 +    if (bRerunMD)
 +    {
 +        if (getenv("GMX_FORCE_UPDATE"))
 +        {
 +            bForceUpdate = TRUE;
 +        }
 +
 +        rerun_fr.natoms = 0;
 +        if (MASTER(cr))
 +        {
 +            bNotLastFrame = read_first_frame(oenv, &status,
 +                                             opt2fn("-rerun", nfile, fnm),
 +                                             &rerun_fr, TRX_NEED_X | TRX_READ_V);
 +            if (rerun_fr.natoms != top_global->natoms)
 +            {
 +                gmx_fatal(FARGS,
 +                          "Number of atoms in trajectory (%d) does not match the "
 +                          "run input file (%d)\n",
 +                          rerun_fr.natoms, top_global->natoms);
 +            }
 +            if (ir->ePBC != epbcNONE)
 +            {
 +                if (!rerun_fr.bBox)
 +                {
 +                    gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f does not contain a box, while pbc is used", rerun_fr.step, rerun_fr.time);
 +                }
 +                if (max_cutoff2(ir->ePBC, rerun_fr.box) < sqr(fr->rlistlong))
 +                {
 +                    gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f has too small box dimensions", rerun_fr.step, rerun_fr.time);
 +                }
 +            }
 +        }
 +
 +        if (PAR(cr))
 +        {
 +            rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame);
 +        }
 +
 +        if (ir->ePBC != epbcNONE)
 +        {
 +            /* Set the shift vectors.
 +             * Necessary here when have a static box different from the tpr box.
 +             */
 +            calc_shifts(rerun_fr.box, fr->shift_vec);
 +        }
 +    }
 +
 +    /* loop over MD steps or if rerunMD to end of input trajectory */
 +    bFirstStep = TRUE;
 +    /* Skip the first Nose-Hoover integration when we get the state from tpx */
 +    bStateFromTPX    = !bStateFromCP;
 +    bInitStep        = bFirstStep && (bStateFromTPX || bVV);
 +    bStartingFromCpt = (Flags & MD_STARTFROMCPT) && bInitStep;
 +    bLastStep        = FALSE;
 +    bSumEkinhOld     = FALSE;
 +    bExchanged       = FALSE;
 +    bNeedRepartition = FALSE;
 +
 +    init_global_signals(&gs, cr, ir, repl_ex_nst);
 +
 +    step     = ir->init_step;
 +    step_rel = 0;
 +
 +    if (ir->nstlist == -1)
 +    {
 +        init_nlistheuristics(&nlh, bGStatEveryStep, step);
 +    }
 +
 +    if (MULTISIM(cr) && (repl_ex_nst <= 0 ))
 +    {
 +        /* check how many steps are left in other sims */
 +        multisim_nsteps = get_multisim_nsteps(cr, ir->nsteps);
 +    }
 +
 +
 +    /* and stop now if we should */
 +    bLastStep = (bRerunMD || (ir->nsteps >= 0 && step_rel > ir->nsteps) ||
 +                 ((multisim_nsteps >= 0) && (step_rel >= multisim_nsteps )));
 +    while (!bLastStep || (bRerunMD && bNotLastFrame))
 +    {
 +
 +        wallcycle_start(wcycle, ewcSTEP);
 +
 +        if (bRerunMD)
 +        {
 +            if (rerun_fr.bStep)
 +            {
 +                step     = rerun_fr.step;
 +                step_rel = step - ir->init_step;
 +            }
 +            if (rerun_fr.bTime)
 +            {
 +                t = rerun_fr.time;
 +            }
 +            else
 +            {
 +                t = step;
 +            }
 +        }
 +        else
 +        {
 +            bLastStep = (step_rel == ir->nsteps);
 +            t         = t0 + step*ir->delta_t;
 +        }
 +
 +        if (ir->efep != efepNO || ir->bSimTemp)
 +        {
 +            /* find and set the current lambdas.  If rerunning, we either read in a state, or a lambda value,
 +               requiring different logic. */
 +
 +            set_current_lambdas(step, ir->fepvals, bRerunMD, &rerun_fr, state_global, state, lam0);
 +            bDoDHDL      = do_per_step(step, ir->fepvals->nstdhdl);
 +            bDoFEP       = (do_per_step(step, nstfep) && (ir->efep != efepNO));
 +            bDoExpanded  = (do_per_step(step, ir->expandedvals->nstexpanded)
 +                            && (ir->bExpanded) && (step > 0) && (!bStartingFromCpt));
 +        }
 +
 +        if (bSimAnn)
 +        {
 +            update_annealing_target_temp(&(ir->opts), t);
 +        }
 +
 +        if (bRerunMD)
 +        {
 +            if (!DOMAINDECOMP(cr) || MASTER(cr))
 +            {
 +                for (i = 0; i < state_global->natoms; i++)
 +                {
 +                    copy_rvec(rerun_fr.x[i], state_global->x[i]);
 +                }
 +                if (rerun_fr.bV)
 +                {
 +                    for (i = 0; i < state_global->natoms; i++)
 +                    {
 +                        copy_rvec(rerun_fr.v[i], state_global->v[i]);
 +                    }
 +                }
 +                else
 +                {
 +                    for (i = 0; i < state_global->natoms; i++)
 +                    {
 +                        clear_rvec(state_global->v[i]);
 +                    }
 +                    if (bRerunWarnNoV)
 +                    {
 +                        fprintf(stderr, "\nWARNING: Some frames do not contain velocities.\n"
 +                                "         Ekin, temperature and pressure are incorrect,\n"
 +                                "         the virial will be incorrect when constraints are present.\n"
 +                                "\n");
 +                        bRerunWarnNoV = FALSE;
 +                    }
 +                }
 +            }
 +            copy_mat(rerun_fr.box, state_global->box);
 +            copy_mat(state_global->box, state->box);
 +
 +            if (vsite && (Flags & MD_RERUN_VSITE))
 +            {
 +                if (DOMAINDECOMP(cr))
 +                {
 +                    gmx_fatal(FARGS, "Vsite recalculation with -rerun is not implemented with domain decomposition, use a single rank");
 +                }
 +                if (graph)
 +                {
 +                    /* Following is necessary because the graph may get out of sync
 +                     * with the coordinates if we only have every N'th coordinate set
 +                     */
 +                    mk_mshift(fplog, graph, fr->ePBC, state->box, state->x);
 +                    shift_self(graph, state->box, state->x);
 +                }
 +                construct_vsites(vsite, state->x, ir->delta_t, state->v,
 +                                 top->idef.iparams, top->idef.il,
 +                                 fr->ePBC, fr->bMolPBC, cr, state->box);
 +                if (graph)
 +                {
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +            }
 +        }
 +
 +        /* Stop Center of Mass motion */
 +        bStopCM = (ir->comm_mode != ecmNO && do_per_step(step, ir->nstcomm));
 +
 +        if (bRerunMD)
 +        {
 +            /* for rerun MD always do Neighbour Searching */
 +            bNS      = (bFirstStep || ir->nstlist != 0);
 +            bNStList = bNS;
 +        }
 +        else
 +        {
 +            /* Determine whether or not to do Neighbour Searching and LR */
 +            bNStList = (ir->nstlist > 0  && step % ir->nstlist == 0);
 +
 +            bNS = (bFirstStep || bExchanged || bNeedRepartition || bNStList || bDoFEP ||
 +                   (ir->nstlist == -1 && nlh.nabnsb > 0));
 +
 +            if (bNS && ir->nstlist == -1)
 +            {
 +                set_nlistheuristics(&nlh, bFirstStep || bExchanged || bNeedRepartition || bDoFEP, step);
 +            }
 +        }
 +
 +        /* check whether we should stop because another simulation has
 +           stopped. */
 +        if (MULTISIM(cr))
 +        {
 +            if ( (multisim_nsteps >= 0) &&  (step_rel >= multisim_nsteps)  &&
 +                 (multisim_nsteps != ir->nsteps) )
 +            {
 +                if (bNS)
 +                {
 +                    if (MASTER(cr))
 +                    {
 +                        fprintf(stderr,
 +                                "Stopping simulation %d because another one has finished\n",
 +                                cr->ms->sim);
 +                    }
 +                    bLastStep         = TRUE;
 +                    gs.sig[eglsCHKPT] = 1;
 +                }
 +            }
 +        }
 +
 +        /* < 0 means stop at next step, > 0 means stop at next NS step */
 +        if ( (gs.set[eglsSTOPCOND] < 0) ||
 +             ( (gs.set[eglsSTOPCOND] > 0) && (bNStList || ir->nstlist == 0) ) )
 +        {
 +            bLastStep = TRUE;
 +        }
 +
 +        /* Determine whether or not to update the Born radii if doing GB */
 +        bBornRadii = bFirstStep;
 +        if (ir->implicit_solvent && (step % ir->nstgbradii == 0))
 +        {
 +            bBornRadii = TRUE;
 +        }
 +
 +        do_log     = do_per_step(step, ir->nstlog) || bFirstStep || bLastStep;
 +        do_verbose = bVerbose &&
 +            (step % stepout == 0 || bFirstStep || bLastStep);
 +
 +        if (bNS && !(bFirstStep && ir->bContinuation && !bRerunMD))
 +        {
 +            if (bRerunMD)
 +            {
 +                bMasterState = TRUE;
 +            }
 +            else
 +            {
 +                bMasterState = FALSE;
 +                /* Correct the new box if it is too skewed */
 +                if (DYNAMIC_BOX(*ir))
 +                {
 +                    if (correct_box(fplog, step, state->box, graph))
 +                    {
 +                        bMasterState = TRUE;
 +                    }
 +                }
 +                if (DOMAINDECOMP(cr) && bMasterState)
 +                {
 +                    dd_collect_state(cr->dd, state, state_global);
 +                }
 +            }
 +
 +            if (DOMAINDECOMP(cr))
 +            {
 +                /* Repartition the domain decomposition */
 +                wallcycle_start(wcycle, ewcDOMDEC);
 +                dd_partition_system(fplog, step, cr,
 +                                    bMasterState, nstglobalcomm,
 +                                    state_global, top_global, ir,
 +                                    state, &f, mdatoms, top, fr,
 +                                    vsite, shellfc, constr,
 +                                    nrnb, wcycle,
 +                                    do_verbose && !bPMETuneRunning);
 +                wallcycle_stop(wcycle, ewcDOMDEC);
 +                /* If using an iterative integrator, reallocate space to match the decomposition */
 +            }
 +        }
 +
 +        if (MASTER(cr) && do_log)
 +        {
 +            print_ebin_header(fplog, step, t, state->lambda[efptFEP]); /* can we improve the information printed here? */
 +        }
 +
 +        if (ir->efep != efepNO)
 +        {
 +            update_mdatoms(mdatoms, state->lambda[efptMASS]);
 +        }
 +
 +        if ((bRerunMD && rerun_fr.bV) || bExchanged)
 +        {
 +
 +            /* We need the kinetic energy at minus the half step for determining
 +             * the full step kinetic energy and possibly for T-coupling.*/
 +            /* This may not be quite working correctly yet . . . . */
 +            compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                            wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot,
 +                            constr, NULL, FALSE, state->box,
 +                            top_global, &bSumEkinhOld,
 +                            CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
 +        }
 +        clear_mat(force_vir);
 +
 +        /* We write a checkpoint at this MD step when:
 +         * either at an NS step when we signalled through gs,
 +         * or at the last step (but not when we do not want confout),
 +         * but never at the first step or with rerun.
 +         */
 +        bCPT = (((gs.set[eglsCHKPT] && (bNS || ir->nstlist == 0)) ||
 +                 (bLastStep && (Flags & MD_CONFOUT))) &&
 +                step > ir->init_step && !bRerunMD);
 +        if (bCPT)
 +        {
 +            gs.set[eglsCHKPT] = 0;
 +        }
 +
 +        /* Determine the energy and pressure:
 +         * at nstcalcenergy steps and at energy output steps (set below).
 +         */
 +        if (EI_VV(ir->eI) && (!bInitStep))
 +        {
 +            /* for vv, the first half of the integration actually corresponds
 +               to the previous step.  bCalcEner is only required to be evaluated on the 'next' step,
 +               but the virial needs to be calculated on both the current step and the 'next' step. Future
 +               reorganization may be able to get rid of one of the bCalcVir=TRUE steps. */
 +
 +            bCalcEner = do_per_step(step-1, ir->nstcalcenergy);
 +            bCalcVir  = bCalcEner ||
 +                (ir->epc != epcNO && (do_per_step(step, ir->nstpcouple) || do_per_step(step-1, ir->nstpcouple)));
 +        }
 +        else
 +        {
 +            bCalcEner = do_per_step(step, ir->nstcalcenergy);
 +            bCalcVir  = bCalcEner ||
 +                (ir->epc != epcNO && do_per_step(step, ir->nstpcouple));
 +        }
 +
 +        /* Do we need global communication ? */
 +        bGStat = (bCalcVir || bCalcEner || bStopCM ||
 +                  do_per_step(step, nstglobalcomm) || (bVV && IR_NVT_TROTTER(ir) && do_per_step(step-1, nstglobalcomm)) ||
 +                  (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck));
 +
 +        do_ene = (do_per_step(step, ir->nstenergy) || bLastStep);
 +
 +        if (do_ene || do_log)
 +        {
 +            bCalcVir  = TRUE;
 +            bCalcEner = TRUE;
 +            bGStat    = TRUE;
 +        }
 +
 +        /* these CGLO_ options remain the same throughout the iteration */
 +        cglo_flags = ((bRerunMD ? CGLO_RERUNMD : 0) |
 +                      (bGStat ? CGLO_GSTAT : 0)
 +                      );
 +
 +        force_flags = (GMX_FORCE_STATECHANGED |
 +                       ((DYNAMIC_BOX(*ir) || bRerunMD) ? GMX_FORCE_DYNAMICBOX : 0) |
 +                       GMX_FORCE_ALLFORCES |
 +                       GMX_FORCE_SEPLRF |
 +                       (bCalcVir ? GMX_FORCE_VIRIAL : 0) |
 +                       (bCalcEner ? GMX_FORCE_ENERGY : 0) |
 +                       (bDoFEP ? GMX_FORCE_DHDL : 0)
 +                       );
 +
 +        if (fr->bTwinRange)
 +        {
 +            if (do_per_step(step, ir->nstcalclr))
 +            {
 +                force_flags |= GMX_FORCE_DO_LR;
 +            }
 +        }
 +
 +        if (shellfc)
 +        {
 +            /* Now is the time to relax the shells */
 +            count = relax_shell_flexcon(fplog, cr, bVerbose, step,
 +                                        ir, bNS, force_flags,
 +                                        top,
 +                                        constr, enerd, fcd,
 +                                        state, f, force_vir, mdatoms,
 +                                        nrnb, wcycle, graph, groups,
 +                                        shellfc, fr, bBornRadii, t, mu_tot,
 +                                        &bConverged, vsite,
 +                                        mdoutf_get_fp_field(outf));
 +            tcount += count;
 +
 +            if (bConverged)
 +            {
 +                nconverged++;
 +            }
 +        }
 +        else
 +        {
 +            /* The coordinates (x) are shifted (to get whole molecules)
 +             * in do_force.
 +             * This is parallellized as well, and does communication too.
 +             * Check comments in sim_util.c
 +             */
 +            do_force(fplog, cr, ir, step, nrnb, wcycle, top, groups,
 +                     state->box, state->x, &state->hist,
 +                     f, force_vir, mdatoms, enerd, fcd,
 +                     state->lambda, graph,
 +                     fr, vsite, mu_tot, t, mdoutf_get_fp_field(outf), ed, bBornRadii,
 +                     (bNS ? GMX_FORCE_NS : 0) | force_flags);
 +        }
 +
 +        if (bVV && !bStartingFromCpt && !bRerunMD)
 +        /*  ############### START FIRST UPDATE HALF-STEP FOR VV METHODS############### */
 +        {
 +            if (ir->eI == eiVV && bInitStep)
 +            {
 +                /* if using velocity verlet with full time step Ekin,
 +                 * take the first half step only to compute the
 +                 * virial for the first step. From there,
 +                 * revert back to the initial coordinates
 +                 * so that the input is actually the initial step.
 +                 */
 +                copy_rvecn(state->v, cbuf, 0, state->natoms); /* should make this better for parallelizing? */
 +            }
 +            else
 +            {
 +                /* this is for NHC in the Ekin(t+dt/2) version of vv */
 +                trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ1);
 +            }
 +
 +            /* If we are using twin-range interactions where the long-range component
 +             * is only evaluated every nstcalclr>1 steps, we should do a special update
 +             * step to combine the long-range forces on these steps.
 +             * For nstcalclr=1 this is not done, since the forces would have been added
 +             * directly to the short-range forces already.
++             *
++             * TODO Remove various aspects of VV+twin-range in master
++             * branch, because VV integrators did not ever support
++             * twin-range multiple time stepping with constraints.
 +             */
 +            bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +            update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC,
 +                          f, bUpdateDoLR, fr->f_twin, fcd,
 +                          ekind, M, upd, bInitStep, etrtVELOCITY1,
 +                          cr, nrnb, constr, &top->idef);
 +
 +            if (bIterativeCase && do_per_step(step-1, ir->nstpcouple) && !bInitStep)
 +            {
 +                gmx_iterate_init(&iterate, TRUE);
 +            }
 +            /* for iterations, we save these vectors, as we will be self-consistently iterating
 +               the calculations */
 +
 +            /*#### UPDATE EXTENDED VARIABLES IN TROTTER FORMULATION */
 +
 +            /* save the state */
 +            if (iterate.bIterationActive)
 +            {
 +                copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts));
 +            }
 +
 +            bFirstIterate = TRUE;
 +            while (bFirstIterate || iterate.bIterationActive)
 +            {
 +                if (iterate.bIterationActive)
 +                {
 +                    copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts));
 +                    if (bFirstIterate && bTrotter)
 +                    {
 +                        /* The first time through, we need a decent first estimate
 +                           of veta(t+dt) to compute the constraints.  Do
 +                           this by computing the box volume part of the
 +                           trotter integration at this time. Nothing else
 +                           should be changed by this routine here.  If
 +                           !(first time), we start with the previous value
 +                           of veta.  */
 +
 +                        veta_save = state->veta;
 +                        trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ0);
 +                        vetanew     = state->veta;
 +                        state->veta = veta_save;
 +                    }
 +                }
 +
 +                bOK = TRUE;
 +                if (!bRerunMD || rerun_fr.bV || bForceUpdate)     /* Why is rerun_fr.bV here?  Unclear. */
 +                {
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, shake_vir,
 +                                       cr, nrnb, wcycle, upd, constr,
 +                                       TRUE, bCalcVir, vetanew);
 +
 +                    if (!bOK)
 +                    {
 +                        gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
 +                    }
 +
 +                }
 +                else if (graph)
 +                {
 +                    /* Need to unshift here if a do_force has been
 +                       called in the previous step */
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +
 +                /* if VV, compute the pressure and constraints */
 +                /* For VV2, we strictly only need this if using pressure
 +                 * control, but we really would like to have accurate pressures
 +                 * printed out.
 +                 * Think about ways around this in the future?
 +                 * For now, keep this choice in comments.
 +                 */
 +                /*bPres = (ir->eI==eiVV || IR_NPT_TROTTER(ir)); */
 +                /*bTemp = ((ir->eI==eiVV &&(!bInitStep)) || (ir->eI==eiVVAK && IR_NPT_TROTTER(ir)));*/
 +                bPres = TRUE;
 +                bTemp = ((ir->eI == eiVV && (!bInitStep)) || (ir->eI == eiVVAK));
 +                if (bCalcEner && ir->eI == eiVVAK)  /*MRS:  7/9/2010 -- this still doesn't fix it?*/
 +                {
 +                    bSumEkinhOld = TRUE;
 +                }
 +                /* for vv, the first half of the integration actually corresponds to the previous step.
 +                   So we need information from the last step in the first half of the integration */
 +                if (bGStat || do_per_step(step-1, nstglobalcomm))
 +                {
 +                    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                    wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                    constr, NULL, FALSE, state->box,
 +                                    top_global, &bSumEkinhOld,
 +                                    cglo_flags
 +                                    | CGLO_ENERGY
 +                                    | (bTemp ? CGLO_TEMPERATURE : 0)
 +                                    | (bPres ? CGLO_PRESSURE : 0)
 +                                    | (bPres ? CGLO_CONSTRAINT : 0)
 +                                    | ((iterate.bIterationActive) ? CGLO_ITERATE : 0)
 +                                    | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
 +                                    | CGLO_SCALEEKIN
 +                                    );
 +                    /* explanation of above:
 +                       a) We compute Ekin at the full time step
 +                       if 1) we are using the AveVel Ekin, and it's not the
 +                       initial step, or 2) if we are using AveEkin, but need the full
 +                       time step kinetic energy for the pressure (always true now, since we want accurate statistics).
 +                       b) If we are using EkinAveEkin for the kinetic energy for the temperature control, we still feed in
 +                       EkinAveVel because it's needed for the pressure */
 +                }
 +                /* temperature scaling and pressure scaling to produce the extended variables at t+dt */
 +                if (!bInitStep)
 +                {
 +                    if (bTrotter)
 +                    {
 +                        m_add(force_vir, shake_vir, total_vir); /* we need the un-dispersion corrected total vir here */
 +                        trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ2);
 +                    }
 +                    else
 +                    {
 +                        if (bExchanged)
 +                        {
 +
 +                            /* We need the kinetic energy at minus the half step for determining
 +                             * the full step kinetic energy and possibly for T-coupling.*/
 +                            /* This may not be quite working correctly yet . . . . */
 +                            compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                            wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot,
 +                                            constr, NULL, FALSE, state->box,
 +                                            top_global, &bSumEkinhOld,
 +                                            CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
 +                        }
 +                    }
 +                }
 +
 +                if (iterate.bIterationActive &&
 +                    done_iterating(cr, fplog, step, &iterate, bFirstIterate,
 +                                   state->veta, &vetanew))
 +                {
 +                    break;
 +                }
 +                bFirstIterate = FALSE;
 +            }
 +
 +            if (bTrotter && !bInitStep)
 +            {
 +                copy_mat(shake_vir, state->svir_prev);
 +                copy_mat(force_vir, state->fvir_prev);
 +                if (IR_NVT_TROTTER(ir) && ir->eI == eiVV)
 +                {
 +                    /* update temperature and kinetic energy now that step is over - this is the v(t+dt) point */
 +                    enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, NULL, (ir->eI == eiVV), FALSE);
 +                    enerd->term[F_EKIN] = trace(ekind->ekin);
 +                }
 +            }
 +            /* if it's the initial step, we performed this first step just to get the constraint virial */
 +            if (bInitStep && ir->eI == eiVV)
 +            {
 +                copy_rvecn(cbuf, state->v, 0, state->natoms);
 +            }
 +        }
 +
 +        /* MRS -- now done iterating -- compute the conserved quantity */
 +        if (bVV)
 +        {
 +            saved_conserved_quantity = compute_conserved_from_auxiliary(ir, state, &MassQ);
 +            if (ir->eI == eiVV)
 +            {
 +                last_ekin = enerd->term[F_EKIN];
 +            }
 +            if ((ir->eDispCorr != edispcEnerPres) && (ir->eDispCorr != edispcAllEnerPres))
 +            {
 +                saved_conserved_quantity -= enerd->term[F_DISPCORR];
 +            }
 +            /* sum up the foreign energy and dhdl terms for vv.  currently done every step so that dhdl is correct in the .edr */
 +            if (!bRerunMD)
 +            {
 +                sum_dhdl(enerd, state->lambda, ir->fepvals);
 +            }
 +        }
 +
 +        /* ########  END FIRST UPDATE STEP  ############## */
 +        /* ########  If doing VV, we now have v(dt) ###### */
 +        if (bDoExpanded)
 +        {
 +            /* perform extended ensemble sampling in lambda - we don't
 +               actually move to the new state before outputting
 +               statistics, but if performing simulated tempering, we
 +               do update the velocities and the tau_t. */
 +
 +            lamnew = ExpandedEnsembleDynamics(fplog, ir, enerd, state, &MassQ, state->fep_state, &state->dfhist, step, state->v, mdatoms);
 +            /* history is maintained in state->dfhist, but state_global is what is sent to trajectory and log output */
 +            copy_df_history(&state_global->dfhist, &state->dfhist);
 +        }
 +
 +        /* Now we have the energies and forces corresponding to the
 +         * coordinates at time t. We must output all of this before
 +         * the update.
 +         */
 +        do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t,
 +                                 ir, state, state_global, top_global, fr,
 +                                 outf, mdebin, ekind, f, f_global,
 +                                 wcycle, &nchkpt,
 +                                 bCPT, bRerunMD, bLastStep, (Flags & MD_CONFOUT),
 +                                 bSumEkinhOld);
 +        /* Check if IMD step and do IMD communication, if bIMD is TRUE. */
 +        bIMDstep = do_IMD(ir->bIMD, step, cr, bNS, state->box, state->x, ir, t, wcycle);
 +
 +        /* kludge -- virial is lost with restart for NPT control. Must restart */
 +        if (bStartingFromCpt && bVV)
 +        {
 +            copy_mat(state->svir_prev, shake_vir);
 +            copy_mat(state->fvir_prev, force_vir);
 +        }
 +
 +        elapsed_time = walltime_accounting_get_current_elapsed_time(walltime_accounting);
 +
 +        /* Check whether everything is still allright */
 +        if (((int)gmx_get_stop_condition() > handled_stop_condition)
 +#ifdef GMX_THREAD_MPI
 +            && MASTER(cr)
 +#endif
 +            )
 +        {
 +            /* this is just make gs.sig compatible with the hack
 +               of sending signals around by MPI_Reduce with together with
 +               other floats */
 +            if (gmx_get_stop_condition() == gmx_stop_cond_next_ns)
 +            {
 +                gs.sig[eglsSTOPCOND] = 1;
 +            }
 +            if (gmx_get_stop_condition() == gmx_stop_cond_next)
 +            {
 +                gs.sig[eglsSTOPCOND] = -1;
 +            }
 +            /* < 0 means stop at next step, > 0 means stop at next NS step */
 +            if (fplog)
 +            {
 +                fprintf(fplog,
 +                        "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
 +                        gmx_get_signal_name(),
 +                        gs.sig[eglsSTOPCOND] == 1 ? "NS " : "");
 +                fflush(fplog);
 +            }
 +            fprintf(stderr,
 +                    "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
 +                    gmx_get_signal_name(),
 +                    gs.sig[eglsSTOPCOND] == 1 ? "NS " : "");
 +            fflush(stderr);
 +            handled_stop_condition = (int)gmx_get_stop_condition();
 +        }
 +        else if (MASTER(cr) && (bNS || ir->nstlist <= 0) &&
 +                 (max_hours > 0 && elapsed_time > max_hours*60.0*60.0*0.99) &&
 +                 gs.sig[eglsSTOPCOND] == 0 && gs.set[eglsSTOPCOND] == 0)
 +        {
 +            /* Signal to terminate the run */
 +            gs.sig[eglsSTOPCOND] = 1;
 +            if (fplog)
 +            {
 +                fprintf(fplog, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99);
 +            }
 +            fprintf(stderr, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99);
 +        }
 +
 +        if (bResetCountersHalfMaxH && MASTER(cr) &&
 +            elapsed_time > max_hours*60.0*60.0*0.495)
 +        {
 +            gs.sig[eglsRESETCOUNTERS] = 1;
 +        }
 +
 +        if (ir->nstlist == -1 && !bRerunMD)
 +        {
 +            /* When bGStatEveryStep=FALSE, global_stat is only called
 +             * when we check the atom displacements, not at NS steps.
 +             * This means that also the bonded interaction count check is not
 +             * performed immediately after NS. Therefore a few MD steps could
 +             * be performed with missing interactions.
 +             * But wrong energies are never written to file,
 +             * since energies are only written after global_stat
 +             * has been called.
 +             */
 +            if (step >= nlh.step_nscheck)
 +            {
 +                nlh.nabnsb = natoms_beyond_ns_buffer(ir, fr, &top->cgs,
 +                                                     nlh.scale_tot, state->x);
 +            }
 +            else
 +            {
 +                /* This is not necessarily true,
 +                 * but step_nscheck is determined quite conservatively.
 +                 */
 +                nlh.nabnsb = 0;
 +            }
 +        }
 +
 +        /* In parallel we only have to check for checkpointing in steps
 +         * where we do global communication,
 +         *  otherwise the other nodes don't know.
 +         */
 +        if (MASTER(cr) && ((bGStat || !PAR(cr)) &&
 +                           cpt_period >= 0 &&
 +                           (cpt_period == 0 ||
 +                            elapsed_time >= nchkpt*cpt_period*60.0)) &&
 +            gs.set[eglsCHKPT] == 0)
 +        {
 +            gs.sig[eglsCHKPT] = 1;
 +        }
 +
 +        /* at the start of step, randomize or scale the velocities (trotter done elsewhere) */
 +        if (EI_VV(ir->eI))
 +        {
 +            if (!bInitStep)
 +            {
 +                update_tcouple(step, ir, state, ekind, &MassQ, mdatoms);
 +            }
 +            if (ETC_ANDERSEN(ir->etc)) /* keep this outside of update_tcouple because of the extra info required to pass */
 +            {
 +                gmx_bool bIfRandomize;
 +                bIfRandomize = update_randomize_velocities(ir, step, cr, mdatoms, state, upd, constr);
 +                /* if we have constraints, we have to remove the kinetic energy parallel to the bonds */
 +                if (constr && bIfRandomize)
 +                {
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, tmp_vir,
 +                                       cr, nrnb, wcycle, upd, constr,
 +                                       TRUE, bCalcVir, vetanew);
 +                }
 +            }
 +        }
 +
 +        if (bIterativeCase && do_per_step(step, ir->nstpcouple))
 +        {
 +            gmx_iterate_init(&iterate, TRUE);
 +            /* for iterations, we save these vectors, as we will be redoing the calculations */
 +            copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts));
 +        }
 +
 +        bFirstIterate = TRUE;
 +        while (bFirstIterate || iterate.bIterationActive)
 +        {
 +            /* We now restore these vectors to redo the calculation with improved extended variables */
 +            if (iterate.bIterationActive)
 +            {
 +                copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts));
 +            }
 +
 +            /* We make the decision to break or not -after- the calculation of Ekin and Pressure,
 +               so scroll down for that logic */
 +
 +            /* #########   START SECOND UPDATE STEP ################# */
 +            /* Box is changed in update() when we do pressure coupling,
 +             * but we should still use the old box for energy corrections and when
 +             * writing it to the energy file, so it matches the trajectory files for
 +             * the same timestep above. Make a copy in a separate array.
 +             */
 +            copy_mat(state->box, lastbox);
 +
 +            bOK         = TRUE;
 +            dvdl_constr = 0;
 +
 +            if (!(bRerunMD && !rerun_fr.bV && !bForceUpdate))
 +            {
 +                wallcycle_start(wcycle, ewcUPDATE);
 +                /* UPDATE PRESSURE VARIABLES IN TROTTER FORMULATION WITH CONSTRAINTS */
 +                if (bTrotter)
 +                {
 +                    if (iterate.bIterationActive)
 +                    {
 +                        if (bFirstIterate)
 +                        {
 +                            scalevir = 1;
 +                        }
 +                        else
 +                        {
 +                            /* we use a new value of scalevir to converge the iterations faster */
 +                            scalevir = tracevir/trace(shake_vir);
 +                        }
 +                        msmul(shake_vir, scalevir, shake_vir);
 +                        m_add(force_vir, shake_vir, total_vir);
 +                        clear_mat(shake_vir);
 +                    }
 +                    trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ3);
 +                    /* We can only do Berendsen coupling after we have summed
 +                     * the kinetic energy or virial. Since the happens
 +                     * in global_state after update, we should only do it at
 +                     * step % nstlist = 1 with bGStatEveryStep=FALSE.
 +                     */
 +                }
 +                else
 +                {
 +                    update_tcouple(step, ir, state, ekind, &MassQ, mdatoms);
 +                    update_pcouple(fplog, step, ir, state, pcoupl_mu, M, bInitStep);
 +                }
 +
 +                if (bVV)
 +                {
 +                    bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                    /* velocity half-step update */
 +                    update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                                  bUpdateDoLR, fr->f_twin, fcd,
 +                                  ekind, M, upd, FALSE, etrtVELOCITY2,
 +                                  cr, nrnb, constr, &top->idef);
 +                }
 +
 +                /* Above, initialize just copies ekinh into ekin,
 +                 * it doesn't copy position (for VV),
 +                 * and entire integrator for MD.
 +                 */
 +
 +                if (ir->eI == eiVVAK)
 +                {
 +                    copy_rvecn(state->x, cbuf, 0, state->natoms);
 +                }
 +                bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                              bUpdateDoLR, fr->f_twin, fcd,
 +                              ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef);
 +                wallcycle_stop(wcycle, ewcUPDATE);
 +
 +                update_constraints(fplog, step, &dvdl_constr, ir, ekind, mdatoms, state,
 +                                   fr->bMolPBC, graph, f,
 +                                   &top->idef, shake_vir,
 +                                   cr, nrnb, wcycle, upd, constr,
 +                                   FALSE, bCalcVir, state->veta);
 +
 +                if (ir->eI == eiVVAK)
 +                {
 +                    /* erase F_EKIN and F_TEMP here? */
 +                    /* just compute the kinetic energy at the half step to perform a trotter step */
 +                    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                    wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                    constr, NULL, FALSE, lastbox,
 +                                    top_global, &bSumEkinhOld,
 +                                    cglo_flags | CGLO_TEMPERATURE
 +                                    );
 +                    wallcycle_start(wcycle, ewcUPDATE);
 +                    trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ4);
 +                    /* now we know the scaling, we can compute the positions again again */
 +                    copy_rvecn(cbuf, state->x, 0, state->natoms);
 +
 +                    bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                    update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                                  bUpdateDoLR, fr->f_twin, fcd,
 +                                  ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef);
 +                    wallcycle_stop(wcycle, ewcUPDATE);
 +
 +                    /* do we need an extra constraint here? just need to copy out of state->v to upd->xp? */
 +                    /* are the small terms in the shake_vir here due
 +                     * to numerical errors, or are they important
 +                     * physically? I'm thinking they are just errors, but not completely sure.
 +                     * For now, will call without actually constraining, constr=NULL*/
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, tmp_vir,
 +                                       cr, nrnb, wcycle, upd, NULL,
 +                                       FALSE, bCalcVir,
 +                                       state->veta);
 +                }
 +                if (!bOK)
 +                {
 +                    gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
 +                }
 +
 +                if (fr->bSepDVDL && fplog && do_log)
 +                {
 +                    gmx_print_sepdvdl(fplog, "Constraint dV/dl", 0.0, dvdl_constr);
 +                }
 +                if (bVV)
 +                {
 +                    /* this factor or 2 correction is necessary
 +                       because half of the constraint force is removed
 +                       in the vv step, so we have to double it.  See
 +                       the Redmine issue #1255.  It is not yet clear
 +                       if the factor of 2 is exact, or just a very
 +                       good approximation, and this will be
 +                       investigated.  The next step is to see if this
 +                       can be done adding a dhdl contribution from the
 +                       rattle step, but this is somewhat more
 +                       complicated with the current code. Will be
 +                       investigated, hopefully for 4.6.3. However,
 +                       this current solution is much better than
 +                       having it completely wrong.
 +                     */
 +                    enerd->term[F_DVDL_CONSTR] += 2*dvdl_constr;
 +                }
 +                else
 +                {
 +                    enerd->term[F_DVDL_CONSTR] += dvdl_constr;
 +                }
 +            }
 +            else if (graph)
 +            {
 +                /* Need to unshift here */
 +                unshift_self(graph, state->box, state->x);
 +            }
 +
 +            if (vsite != NULL)
 +            {
 +                wallcycle_start(wcycle, ewcVSITECONSTR);
 +                if (graph != NULL)
 +                {
 +                    shift_self(graph, state->box, state->x);
 +                }
 +                construct_vsites(vsite, state->x, ir->delta_t, state->v,
 +                                 top->idef.iparams, top->idef.il,
 +                                 fr->ePBC, fr->bMolPBC, cr, state->box);
 +
 +                if (graph != NULL)
 +                {
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +                wallcycle_stop(wcycle, ewcVSITECONSTR);
 +            }
 +
 +            /* ############## IF NOT VV, Calculate globals HERE, also iterate constraints  ############ */
 +            /* With Leap-Frog we can skip compute_globals at
 +             * non-communication steps, but we need to calculate
 +             * the kinetic energy one step before communication.
 +             */
 +            if (bGStat || (!EI_VV(ir->eI) && do_per_step(step+1, nstglobalcomm)))
 +            {
 +                if (ir->nstlist == -1 && bFirstIterate)
 +                {
 +                    gs.sig[eglsNABNSB] = nlh.nabnsb;
 +                }
 +                compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                constr,
 +                                bFirstIterate ? &gs : NULL,
 +                                (step_rel % gs.nstms == 0) &&
 +                                (multisim_nsteps < 0 || (step_rel < multisim_nsteps)),
 +                                lastbox,
 +                                top_global, &bSumEkinhOld,
 +                                cglo_flags
 +                                | (!EI_VV(ir->eI) || bRerunMD ? CGLO_ENERGY : 0)
 +                                | (!EI_VV(ir->eI) && bStopCM ? CGLO_STOPCM : 0)
 +                                | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0)
 +                                | (!EI_VV(ir->eI) || bRerunMD ? CGLO_PRESSURE : 0)
 +                                | (iterate.bIterationActive ? CGLO_ITERATE : 0)
 +                                | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
 +                                | CGLO_CONSTRAINT
 +                                );
 +                if (ir->nstlist == -1 && bFirstIterate)
 +                {
 +                    nlh.nabnsb         = gs.set[eglsNABNSB];
 +                    gs.set[eglsNABNSB] = 0;
 +                }
 +            }
 +            /* bIterate is set to keep it from eliminating the old ekin kinetic energy terms */
 +            /* #############  END CALC EKIN AND PRESSURE ################# */
 +
 +            /* Note: this is OK, but there are some numerical precision issues with using the convergence of
 +               the virial that should probably be addressed eventually. state->veta has better properies,
 +               but what we actually need entering the new cycle is the new shake_vir value. Ideally, we could
 +               generate the new shake_vir, but test the veta value for convergence.  This will take some thought. */
 +
 +            if (iterate.bIterationActive &&
 +                done_iterating(cr, fplog, step, &iterate, bFirstIterate,
 +                               trace(shake_vir), &tracevir))
 +            {
 +                break;
 +            }
 +            bFirstIterate = FALSE;
 +        }
 +
 +        if (!bVV || bRerunMD)
 +        {
 +            /* sum up the foreign energy and dhdl terms for md and sd. currently done every step so that dhdl is correct in the .edr */
 +            sum_dhdl(enerd, state->lambda, ir->fepvals);
 +        }
 +        update_box(fplog, step, ir, mdatoms, state, f,
 +                   ir->nstlist == -1 ? &nlh.scale_tot : NULL, pcoupl_mu, nrnb, upd);
 +
 +        /* ################# END UPDATE STEP 2 ################# */
 +        /* #### We now have r(t+dt) and v(t+dt/2)  ############# */
 +
 +        /* The coordinates (x) were unshifted in update */
 +        if (!bGStat)
 +        {
 +            /* We will not sum ekinh_old,
 +             * so signal that we still have to do it.
 +             */
 +            bSumEkinhOld = TRUE;
 +        }
 +
 +        /* #########  BEGIN PREPARING EDR OUTPUT  ###########  */
 +
 +        /* use the directly determined last velocity, not actually the averaged half steps */
 +        if (bTrotter && ir->eI == eiVV)
 +        {
 +            enerd->term[F_EKIN] = last_ekin;
 +        }
 +        enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
 +
 +        if (bVV)
 +        {
 +            enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + saved_conserved_quantity;
 +        }
 +        else
 +        {
 +            enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + compute_conserved_from_auxiliary(ir, state, &MassQ);
 +        }
 +        /* #########  END PREPARING EDR OUTPUT  ###########  */
 +
 +        /* Output stuff */
 +        if (MASTER(cr))
 +        {
 +            gmx_bool do_dr, do_or;
 +
 +            if (fplog && do_log && bDoExpanded)
 +            {
 +                /* only needed if doing expanded ensemble */
 +                PrintFreeEnergyInfoToFile(fplog, ir->fepvals, ir->expandedvals, ir->bSimTemp ? ir->simtempvals : NULL,
 +                                          &state_global->dfhist, state->fep_state, ir->nstlog, step);
 +            }
 +            if (!(bStartingFromCpt && (EI_VV(ir->eI))))
 +            {
 +                if (bCalcEner)
 +                {
 +                    upd_mdebin(mdebin, bDoDHDL, TRUE,
 +                               t, mdatoms->tmass, enerd, state,
 +                               ir->fepvals, ir->expandedvals, lastbox,
 +                               shake_vir, force_vir, total_vir, pres,
 +                               ekind, mu_tot, constr);
 +                }
 +                else
 +                {
 +                    upd_mdebin_step(mdebin);
 +                }
 +
 +                do_dr  = do_per_step(step, ir->nstdisreout);
 +                do_or  = do_per_step(step, ir->nstorireout);
 +
 +                print_ebin(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or, do_log ? fplog : NULL,
 +                           step, t,
 +                           eprNORMAL, bCompact, mdebin, fcd, groups, &(ir->opts));
 +            }
 +            if (ir->ePull != epullNO)
 +            {
 +                pull_print_output(ir->pull, step, t);
 +            }
 +
 +            if (do_per_step(step, ir->nstlog))
 +            {
 +                if (fflush(fplog) != 0)
 +                {
 +                    gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?");
 +                }
 +            }
 +        }
 +        if (bDoExpanded)
 +        {
 +            /* Have to do this part _after_ outputting the logfile and the edr file */
 +            /* Gets written into the state at the beginning of next loop*/
 +            state->fep_state = lamnew;
 +        }
 +        /* Print the remaining wall clock time for the run */
 +        if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal()) && !bPMETuneRunning)
 +        {
 +            if (shellfc)
 +            {
 +                fprintf(stderr, "\n");
 +            }
 +            print_time(stderr, walltime_accounting, step, ir, cr);
 +        }
 +
 +        /* Ion/water position swapping.
 +         * Not done in last step since trajectory writing happens before this call
 +         * in the MD loop and exchanges would be lost anyway. */
 +        bNeedRepartition = FALSE;
 +        if ((ir->eSwapCoords != eswapNO) && (step > 0) && !bLastStep &&
 +            do_per_step(step, ir->swap->nstswap))
 +        {
 +            bNeedRepartition = do_swapcoords(cr, step, t, ir, wcycle,
 +                                             bRerunMD ? rerun_fr.x   : state->x,
 +                                             bRerunMD ? rerun_fr.box : state->box,
 +                                             top_global, MASTER(cr) && bVerbose, bRerunMD);
 +
 +            if (bNeedRepartition && DOMAINDECOMP(cr))
 +            {
 +                dd_collect_state(cr->dd, state, state_global);
 +            }
 +        }
 +
 +        /* Replica exchange */
 +        bExchanged = FALSE;
 +        if ((repl_ex_nst > 0) && (step > 0) && !bLastStep &&
 +            do_per_step(step, repl_ex_nst))
 +        {
 +            bExchanged = replica_exchange(fplog, cr, repl_ex,
 +                                          state_global, enerd,
 +                                          state, step, t);
 +        }
 +
 +        if ( (bExchanged || bNeedRepartition) && DOMAINDECOMP(cr) )
 +        {
 +            dd_partition_system(fplog, step, cr, TRUE, 1,
 +                                state_global, top_global, ir,
 +                                state, &f, mdatoms, top, fr,
 +                                vsite, shellfc, constr,
 +                                nrnb, wcycle, FALSE);
 +        }
 +
 +        bFirstStep       = FALSE;
 +        bInitStep        = FALSE;
 +        bStartingFromCpt = FALSE;
 +
 +        /* #######  SET VARIABLES FOR NEXT ITERATION IF THEY STILL NEED IT ###### */
 +        /* With all integrators, except VV, we need to retain the pressure
 +         * at the current step for coupling at the next step.
 +         */
 +        if ((state->flags & (1<<estPRES_PREV)) &&
 +            (bGStatEveryStep ||
 +             (ir->nstpcouple > 0 && step % ir->nstpcouple == 0)))
 +        {
 +            /* Store the pressure in t_state for pressure coupling
 +             * at the next MD step.
 +             */
 +            copy_mat(pres, state->pres_prev);
 +        }
 +
 +        /* #######  END SET VARIABLES FOR NEXT ITERATION ###### */
 +
 +        if ( (membed != NULL) && (!bLastStep) )
 +        {
 +            rescale_membed(step_rel, membed, state_global->x);
 +        }
 +
 +        if (bRerunMD)
 +        {
 +            if (MASTER(cr))
 +            {
 +                /* read next frame from input trajectory */
 +                bNotLastFrame = read_next_frame(oenv, status, &rerun_fr);
 +            }
 +
 +            if (PAR(cr))
 +            {
 +                rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame);
 +            }
 +        }
 +
 +        if (!bRerunMD || !rerun_fr.bStep)
 +        {
 +            /* increase the MD step number */
 +            step++;
 +            step_rel++;
 +        }
 +
 +        cycles = wallcycle_stop(wcycle, ewcSTEP);
 +        if (DOMAINDECOMP(cr) && wcycle)
 +        {
 +            dd_cycles_add(cr->dd, cycles, ddCyclStep);
 +        }
 +
 +        if (bPMETuneRunning || bPMETuneTry)
 +        {
 +            /* PME grid + cut-off optimization with GPUs or PME nodes */
 +
 +            /* Count the total cycles over the last steps */
 +            cycles_pmes += cycles;
 +
 +            /* We can only switch cut-off at NS steps */
 +            if (step % ir->nstlist == 0)
 +            {
 +                /* PME grid + cut-off optimization with GPUs or PME nodes */
 +                if (bPMETuneTry)
 +                {
 +                    if (DDMASTER(cr->dd))
 +                    {
 +                        /* PME node load is too high, start tuning */
 +                        bPMETuneRunning = (dd_pme_f_ratio(cr->dd) >= 1.05);
 +                    }
 +                    dd_bcast(cr->dd, sizeof(gmx_bool), &bPMETuneRunning);
 +
 +                    if (bPMETuneRunning || step_rel > ir->nstlist*50)
 +                    {
 +                        bPMETuneTry     = FALSE;
 +                    }
 +                }
 +                if (bPMETuneRunning)
 +                {
 +                    /* init_step might not be a multiple of nstlist,
 +                     * but the first cycle is always skipped anyhow.
 +                     */
 +                    bPMETuneRunning =
 +                        pme_load_balance(pme_loadbal, cr,
 +                                         (bVerbose && MASTER(cr)) ? stderr : NULL,
 +                                         fplog,
 +                                         ir, state, cycles_pmes,
 +                                         fr->ic, fr->nbv, &fr->pmedata,
 +                                         step);
 +
 +                    /* Update constants in forcerec/inputrec to keep them in sync with fr->ic */
 +                    fr->ewaldcoeff_q  = fr->ic->ewaldcoeff_q;
 +                    fr->ewaldcoeff_lj = fr->ic->ewaldcoeff_lj;
 +                    fr->rlist         = fr->ic->rlist;
 +                    fr->rlistlong     = fr->ic->rlistlong;
 +                    fr->rcoulomb      = fr->ic->rcoulomb;
 +                    fr->rvdw          = fr->ic->rvdw;
 +
 +                    if (ir->eDispCorr != edispcNO)
 +                    {
 +                        calc_enervirdiff(NULL, ir->eDispCorr, fr);
 +                    }
 +                }
 +                cycles_pmes = 0;
 +            }
 +        }
 +
 +        if (step_rel == wcycle_get_reset_counters(wcycle) ||
 +            gs.set[eglsRESETCOUNTERS] != 0)
 +        {
 +            /* Reset all the counters related to performance over the run */
 +            reset_all_counters(fplog, cr, step, &step_rel, ir, wcycle, nrnb, walltime_accounting,
 +                               fr->nbv != NULL && fr->nbv->bUseGPU ? fr->nbv->cu_nbv : NULL);
 +            wcycle_set_reset_counters(wcycle, -1);
 +            if (!(cr->duty & DUTY_PME))
 +            {
 +                /* Tell our PME node to reset its counters */
 +                gmx_pme_send_resetcounters(cr, step);
 +            }
 +            /* Correct max_hours for the elapsed time */
 +            max_hours                -= elapsed_time/(60.0*60.0);
 +            bResetCountersHalfMaxH    = FALSE;
 +            gs.set[eglsRESETCOUNTERS] = 0;
 +        }
 +
 +        /* If bIMD is TRUE, the master updates the IMD energy record and sends positions to VMD client */
 +        IMD_prep_energies_send_positions(ir->bIMD && MASTER(cr), bIMDstep, ir->imd, enerd, step, bCalcEner, wcycle);
 +
 +    }
 +    /* End of main MD loop */
 +    debug_gmx();
 +
 +    /* Stop measuring walltime */
 +    walltime_accounting_end(walltime_accounting);
 +
 +    if (bRerunMD && MASTER(cr))
 +    {
 +        close_trj(status);
 +    }
 +
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Tell the PME only node to finish */
 +        gmx_pme_send_finish(cr);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        if (ir->nstcalcenergy > 0 && !bRerunMD)
 +        {
 +            print_ebin(mdoutf_get_fp_ene(outf), FALSE, FALSE, FALSE, fplog, step, t,
 +                       eprAVER, FALSE, mdebin, fcd, groups, &(ir->opts));
 +        }
 +    }
 +
 +    done_mdoutf(outf);
 +    debug_gmx();
 +
 +    if (ir->nstlist == -1 && nlh.nns > 0 && fplog)
 +    {
 +        fprintf(fplog, "Average neighborlist lifetime: %.1f steps, std.dev.: %.1f steps\n", nlh.s1/nlh.nns, sqrt(nlh.s2/nlh.nns - sqr(nlh.s1/nlh.nns)));
 +        fprintf(fplog, "Average number of atoms that crossed the half buffer length: %.1f\n\n", nlh.ab/nlh.nns);
 +    }
 +
 +    if (pme_loadbal != NULL)
 +    {
 +        pme_loadbal_done(pme_loadbal, cr, fplog,
 +                         fr->nbv != NULL && fr->nbv->bUseGPU);
 +    }
 +
 +    if (shellfc && fplog)
 +    {
 +        fprintf(fplog, "Fraction of iterations that converged:           %.2f %%\n",
 +                (nconverged*100.0)/step_rel);
 +        fprintf(fplog, "Average number of force evaluations per MD step: %.2f\n\n",
 +                tcount/step_rel);
 +    }
 +
 +    if (repl_ex_nst > 0 && MASTER(cr))
 +    {
 +        print_replica_exchange_statistics(fplog, repl_ex);
 +    }
 +
 +    /* IMD cleanup, if bIMD is TRUE. */
 +    IMD_finalize(ir->bIMD, ir->imd);
 +
 +    walltime_accounting_set_nsteps_done(walltime_accounting, step_rel);
 +
 +    return 0;
 +}