Merge branch release-5-1 into release-2016
authorMark Abraham <mark.j.abraham@gmail.com>
Tue, 7 Jun 2016 23:14:01 +0000 (01:14 +0200)
committerSzilárd Páll <pall.szilard@gmail.com>
Wed, 8 Jun 2016 00:33:33 +0000 (02:33 +0200)
Conflicts:
CMakeLists.txt

Adjacent unrelated changes, trivial resolution

cmake/gmxDetectSimd.cmake

Two unrelated fixes, managing the new GMX_STDLIB_LIBRARIES better, and
working around the fact that we can't use try_run with noisy
compilers. The new context invalidates the suggestion to use try_run
once we require CMake 2.8.11.

cmake/gmxSetBuildInformation.cmake

As above.

src/gromacs/commandline/filenm.h

Adjacent changes caused by introducing Doxygen

src/gromacs/fileio/filetypes.cpp

As above

src/gromacs/legacyheaders/force.h

Change to function signature to pass const char *fn.

src/gromacs/mdlib/forcerec.cpp

Minor clashes from reorganized include files.

TODO make_bonded_tables was refactored in release-5-1 to need
t_filenm, which reintroduces a dependency on
commandline/filenm.h. Decide what to do about this.

src/gromacs/mdlib/mdatoms.cpp

git was confused about the frozen-atom fix because of the indenting
change, but the merge is straightforward in terms of code logic.

src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda.cu

New Doxygen in release-2016 clashed with introducing support for CUDA
6.0/6.1.

src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel.cuh

As above

src/gromacs/tables/forcetable.cpp

Change to function signature to pass const char *fn.

src/programs/mdrun/tests/CMakeLists.txt

Adjacent new files introduceed in both branches

Change-Id: Iaaffacc186aa5ff67c83522d2c07b05afeec75b2

21 files changed:
1  2 
CMakeLists.txt
cmake/gmxDetectSimd.cmake
cmake/gmxManageNvccConfig.cmake
cmake/gmxSetBuildInformation.cmake
docs/manual/forcefield.tex
src/gromacs/commandline/filenm.cpp
src/gromacs/commandline/filenm.h
src/gromacs/fileio/filetypes.cpp
src/gromacs/mdlib/forcerec.cpp
src/gromacs/mdlib/forcerec.h
src/gromacs/mdlib/mdatoms.cpp
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda.cu
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel.cuh
src/gromacs/mdlib/update.cpp
src/gromacs/tables/forcetable.cpp
src/gromacs/tables/forcetable.h
src/gromacs/utility/stringutil.h
src/programs/mdrun/mdrun.cpp
src/programs/mdrun/runner.cpp
src/programs/mdrun/tests/CMakeLists.txt
src/programs/mdrun/tests/tabulated_bonded_interactions.cpp

diff --combined CMakeLists.txt
index b05d6867f2b6dc110631e38dd12880d20dba7ad8,4fee4802b608dfc5cdbbab7ead305aa7a6e93fa8..849383540cdf3b2dae1cfef3f61b48a3f0b173a0
@@@ -52,7 -52,6 +52,7 @@@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CM
  # the build tree (whether the build is from a source package or from a git
  # repository).  Also declares a few functions that will be used for generating
  # version info files later.
 +include(gmxBuildTreeInfo)
  include(gmxVersionInfo)
  
  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND UNIX)
@@@ -133,40 -132,6 +133,40 @@@ endif(
  include(gmxDetectTargetArchitecture)
  gmx_detect_target_architecture()
  
 +# Permit the user to specify a particular standard library, e.g. compiling
 +# with "-stdlib=libc++" and linking with "-lc++abi -lc++" to get clang's libcxx.
 +set(GMX_STDLIB_CXX_FLAGS "" CACHE STRING "Compiler flag for a C++ standard library flavour")
 +set(GMX_STDLIB_LIBRARIES "" CACHE STRING "Linker libraries for a particular C++ standard library")
 +mark_as_advanced(GMX_STDLIB_CXX_FLAGS)
 +mark_as_advanced(GMX_STDLIB_LIBRARIES)
 +
 +########################################################################
 +# Detect CXX11 support and flags
 +########################################################################
 +# The cmake/Check{C,CXX}CompilerFlag.cmake files in the GROMACS distribution
 +# are used with permission from CMake v3.0.0 so that GROMACS can detect
 +# invalid options with the Intel Compilers, and we have added a line
 +# to detect warnings with the Fujitsu compilers on K computer and ICC.
 +# CMake-3.0 also has a bug where the FAIL_REGEX pattern for AIX contains
 +# a semicolon. Since this is also used as a separator in lists inside CMake,
 +# that string ends up being split into two separate patterns, and the last
 +# part is just a single word that also matches other messages. We solved this
 +# by replacing the semicolon with a period that matches any character.
 +#
 +# 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)
 +include(CheckCXXCompilerFlag)
 +
 +# This must come early, since some of our configuration flag tests
 +# depend on being able to compile C++11 source files.
 +include(gmxTestCXX11)
 +gmx_test_cxx11(GMX_CXX11_FLAGS GMX_STDLIB_CXX_FLAGS GMX_STDLIB_LIBRARIES)
 +
 +# Make sure all C++ code will be compiled in C++11 mode, with the
 +# expected standard library.
 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GMX_CXX11_FLAGS} ${GMX_STDLIB_CXX_FLAGS}")
 +
  ########################################################################
  # User input options                                                   #
  ########################################################################
@@@ -174,8 -139,12 +174,8 @@@ include(gmxOptionUtilities
  
  set(CMAKE_PREFIX_PATH "" CACHE STRING "Extra locations to search for external libraries and tools (give directory without lib, bin, or include)")
  
 -if(GMX_TARGET_FUJITSU_SPARC64)
 -    # Fujitsu only has SIMD in double precision, so this will be faster
 -    set(GMX_DOUBLE_DEFAULT ON)
 -else()
 -    set(GMX_DOUBLE_DEFAULT OFF)
 -endif()
 +# Fujitsu only has SIMD in double precision, so this will be faster
 +gmx_set_boolean(GMX_DOUBLE_DEFAULT GMX_TARGET_FUJITSU_SPARC64)
  option(GMX_DOUBLE "Use double precision (much slower, use only if you really need it)" ${GMX_DOUBLE_DEFAULT})
  option(GMX_RELAXED_DOUBLE_PRECISION "Accept single precision 1/sqrt(x) when using Fujitsu HPC-ACE SIMD" OFF)
  mark_as_advanced(GMX_RELAXED_DOUBLE_PRECISION)
@@@ -188,6 -157,8 +188,6 @@@ gmx_dependent_option
      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)
  
@@@ -198,11 -169,11 +198,11 @@@ gmx_add_cache_dependency(GMX_COOL_QUOTE
  option(GMX_USE_OPENCL "Enable OpenCL acceleration" OFF)
  
  # Decide on GPU settings based on user-settings and GPU/CUDA detection.
 -# We support CUDA >=v4.0 on *nix, but <= v4.1 doesn't work with MSVC
 +# GCC 4.6 requires CUDA 5.0 and VS2015 requires CUDA 8.0
  if(MSVC)
 -    set(REQUIRED_CUDA_VERSION 4.1)
 +    set(REQUIRED_CUDA_VERSION 8.0)
  else()
 -    set(REQUIRED_CUDA_VERSION 4.0)
 +    set(REQUIRED_CUDA_VERSION 5.0)
  endif()
  set(REQUIRED_CUDA_COMPUTE_CAPABILITY 2.0)
  
  set(REQUIRED_OPENCL_MIN_VERSION 1.1)
  
  if(NOT GMX_USE_OPENCL)
 -    # CUDA detection is done only if GMX_USE_OPENCL is OFF
 +    # CUDA detection is done only if GMX_USE_OPENCL is OFF.
      include(gmxManageGPU)
 +    set(GMX_USE_CUDA ${GMX_GPU})
 +    if(GMX_GPU)
 +        set(GMX_GPU_ACCELERATION_FRAMEWORK "GMX_GPU_CUDA")
 +    else()
 +        set(GMX_GPU_ACCELERATION_FRAMEWORK "GMX_GPU_NONE")
 +    endif()
  else()
 -    #Now the OpenCL path
 +    #Now the OpenCL path (for both AMD and NVIDIA)
      if(GMX_GPU)
          include(gmxManageOpenCL)
 -    else(GMX_GPU)
 +        set(GMX_GPU_ACCELERATION_FRAMEWORK "GMX_GPU_OPENCL")
 +    else()
          message(FATAL_ERROR "OpenCL requested but GPU option is not enabled (try -DGMX_GPU=on) ")
 -    endif(GMX_GPU)
 +    endif()
  endif()
  
  include(gmxDetectSimd)
@@@ -235,7 -199,7 +235,7 @@@ 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 AVX_512F AVX_512ER MIC ARM_NEON ARM_NEON_ASIMD IBM_QPX IBM_VMX IBM_VSX Sparc64_HPC_ACE Reference)
 +    None SSE2 SSE4.1 AVX_128_FMA AVX_256 AVX2_256 AVX_512 AVX_512_KNL MIC ARM_NEON ARM_NEON_ASIMD IBM_QPX IBM_VMX IBM_VSX Sparc64_HPC_ACE Reference)
  
  if(GMX_TARGET_MIC)
      set(GMX_FFT_LIBRARY_DEFAULT "mkl")
@@@ -277,6 -241,7 +277,6 @@@ mark_as_advanced(GMX_BROKEN_CALLOC
  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(GMX_USE_TNG "Use the TNG library for trajectory I/O" ON)
@@@ -299,14 -264,6 +299,14 @@@ option(GMX_DEVELOPER_BUIL
      OFF)
  mark_as_advanced(GMX_DEVELOPER_BUILD)
  
 +gmx_set_boolean(GMX_COMPILER_WARNINGS_DEFAULT "NOT SOURCE_IS_SOURCE_DISTRIBUTION")
 +option(GMX_COMPILER_WARNINGS
 +    "Enable a default set of compiler warnings"
 +    ${GMX_COMPILER_WARNINGS_DEFAULT})
 +mark_as_advanced(GMX_COMPILER_WARNINGS)
 +# Always turn on compiler warnings with a developer build.
 +gmx_add_cache_dependency(GMX_COMPILER_WARNINGS BOOL "NOT GMX_DEVELOPER_BUILD" ON)
 +
  option(GMX_BUILD_SHARED_EXE
      "Build exectuables as shared binaries. If not set, this disables rpath and dynamic linker flags in an attempt to build a static binary, but this may require setting up the toolchain properly and making appropriate libraries available."
      ON)
@@@ -325,6 -282,21 +325,6 @@@ include(gmxManageOpenMP
  # These need to be done early (before further tests).
  #####################################################################
  
 -# The cmake/Check{C,CXX}CompilerFlag.cmake files in the GROMACS distribution
 -# are used with permission from CMake v3.0.0 so that GROMACS can detect
 -# invalid options with the Intel Compilers, and we have added a line
 -# to detect warnings with the Fujitsu compilers on K computer and ICC.
 -# CMake-3.0 also has a bug where the FAIL_REGEX pattern for AIX contains
 -# a semicolon. Since this is also used as a separator in lists inside CMake,
 -# that string ends up being split into two separate patterns, and the last
 -# part is just a single word that also matches other messages. We solved this
 -# by replacing the semicolon with a period that matches any character.
 -#
 -# 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)
 -include(CheckCXXCompilerFlag)
 -
  include(gmxCFlags)
  gmx_c_flags()
  
@@@ -348,22 -320,17 +348,22 @@@ if(GMX_SIMD STREQUAL "AVX_256
      # See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49002
  endif()
  
 -
 +# Implement double-precision option. This is complicated because we
 +# need installed headers to use the precision mode of the build that
 +# produced the library, but cannot use config.h in that case. We also
 +# want such variables to always have a definition, because #if is more
 +# robust than #ifdef. So, we put this value on the compiler command
 +# line in all cases.
 +#
 +# GMX_RELAXED_DOUBLE_PRECISION does not need to be handled here,
 +# because no installed header needs it
  if(GMX_DOUBLE)
 -    add_definitions(-DGMX_DOUBLE)
 -    list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_DOUBLE")
 -    if(GMX_RELAXED_DOUBLE_PRECISION)
 -        add_definitions(-DGMX_RELAXED_DOUBLE_PRECISION)
 -    endif()
 -endif()
 -if(GMX_SOFTWARE_INVSQRT)
 -    list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_SOFTWARE_INVSQRT")
 +    set(GMX_DOUBLE_VALUE 1)
 +else()
 +    set(GMX_DOUBLE_VALUE 0)
  endif()
 +add_definitions(-DGMX_DOUBLE=${GMX_DOUBLE_VALUE})
 +list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_DOUBLE=${GMX_DOUBLE_VALUE}")
  
  if(WIN32)
      list(APPEND GMX_EXTRA_LIBRARIES "wsock32")
@@@ -390,6 -357,9 +390,6 @@@ check_include_files(regex.h      HAVE_P
  # as selections won't be fully functional.
  
  include(CheckCXXSymbolExists)
 -check_cxx_symbol_exists(posix_memalign    stdlib.h     HAVE_POSIX_MEMALIGN)
 -check_cxx_symbol_exists(memalign          stdlib.h     HAVE_MEMALIGN)
 -check_cxx_symbol_exists(_aligned_malloc   stdlib.h     HAVE__ALIGNED_MALLOC)
  check_cxx_symbol_exists(gettimeofday      sys/time.h   HAVE_GETTIMEOFDAY)
  check_cxx_symbol_exists(sysconf           unistd.h     HAVE_SYSCONF)
  check_cxx_symbol_exists(nice              unistd.h     HAVE_NICE)
@@@ -398,41 -368,26 +398,42 @@@ check_cxx_symbol_exists(_filen
  check_cxx_symbol_exists(fileno            stdio.h      HAVE_FILENO)
  check_cxx_symbol_exists(_commit           io.h         HAVE__COMMIT)
  check_cxx_symbol_exists(sigaction         signal.h     HAVE_SIGACTION)
 -check_cxx_symbol_exists(rsqrt             math.h       HAVE_RSQRT)
 -check_cxx_symbol_exists(rsqrtf            math.h       HAVE_RSQRTF)
 -check_cxx_symbol_exists(sqrtf             math.h       HAVE_SQRTF)
 +
 +# We cannot check for the __builtins as symbols, but check if code compiles
 +check_cxx_source_compiles("int main(){ return __builtin_clz(1);}"   HAVE_BUILTIN_CLZ)
 +check_cxx_source_compiles("int main(){ return __builtin_clzll(1);}" HAVE_BUILTIN_CLZLL)
 +if(MSVC)
 +    check_cxx_source_compiles("#include <intrin.h>\n int main(){unsigned long r;unsigned long i=1;_BitScanReverse(&r,i);return r;}" HAVE_BITSCANREVERSE)
 +    check_cxx_source_compiles("#include <intrin.h>\n int main(){unsigned long r;unsigned __int64 i=1;_BitScanReverse(&r,i);return r;}" HAVE_BITSCANREVERSE64)
 +elseif(CMAKE_CXX_COMPILER_ID MATCHES "XL")
 +    check_cxx_source_compiles("int main(){ return __cntlz4(1);}" HAVE_CNTLZ4)
 +    check_cxx_source_compiles("int main(){ return __cntlz8(1);}" HAVE_CNTLZ8)
 +endif()
  
  include(CheckLibraryExists)
  find_library(HAVE_LIBM m)
+ mark_as_advanced(HAVE_LIBM)
  check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME)
  check_library_exists(m feenableexcept "" HAVE_FEENABLEEXCEPT)
  
  include(TestSchedAffinity)
  test_sched_affinity(HAVE_SCHED_AFFINITY)
  
 +# Aligned memory allocation. We need to check for both mm_malloc(),
 +# posix_memalign(), memalign(), and on windows also _aligned_malloc()
 +include(gmxTestMMMalloc)
 +gmx_test_mm_malloc(HAVE__MM_MALLOC)
 +check_cxx_symbol_exists(posix_memalign    stdlib.h     HAVE_POSIX_MEMALIGN)
 +check_cxx_symbol_exists(memalign          stdlib.h     HAVE_MEMALIGN)
 +if(MSVC)
 +    # No need to waste time on this test on platforms where it will never be true
 +    check_cxx_symbol_exists(_aligned_malloc   stdlib.h     HAVE__ALIGNED_MALLOC)
 +endif()
 +
  include(TestBigEndian)
  test_big_endian(GMX_INTEGER_BIG_ENDIAN)
  
 -set(GMX_USE_NICE 0)
 -if (HAVE_UNISTD_H AND HAVE_NICE)
 -    set(GMX_USE_NICE 1)
 -endif()
 +gmx_set_boolean(GMX_USE_NICE "HAVE_UNISTD_H AND HAVE_NICE")
  
  # Management of GROMACS options for specific toolchains should go
  # here. Because the initial settings for some of the main options have
@@@ -474,15 -429,15 +475,15 @@@ gmx_test_zlib(HAVE_ZLIB
  # 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.
 -if(DEFINED LIBXML2_LIBRARIES)
 -  set(LibXml2_FIND_QUIETLY TRUE)
 -endif()
 -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)
 +#if(DEFINED LIBXML2_LIBRARIES)
 +#  set(LibXml2_FIND_QUIETLY TRUE)
 +#endif()
 +#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")
  #    set(XML_LIBRARIES ${LIBXML2_LIBRARIES})
  #endif()
  
 +if(DEFINED HWLOC_LIBRARIES)
 +  set(Hwloc_FIND_QUIETLY TRUE)
 +endif()
 +find_package(Hwloc 1.5)
 +if(HWLOC_FOUND)
 +    set(GMX_HWLOC_DEFAULT ON)
 +else()
 +    set(GMX_HWLOC_DEFAULT OFF)
 +endif()
 +option(GMX_HWLOC "Add support for hwloc Portable Hardware locality library" ${GMX_HWLOC_DEFAULT})
 +if(GMX_HWLOC)
 +    if(HWLOC_FOUND)
 +        include_directories(${HWLOC_INCLUDE_DIRS})
 +        list(APPEND GMX_EXTRA_LIBRARIES ${HWLOC_LIBRARIES})
 +    else()
 +        message(FATAL_ERROR "Hwloc package support requested, but not found.")
 +    endif()
 +endif()
 +
 +option(GMX_EXTERNAL_TINYXML2 "Use external TinyXML-2 instead of compiling the version bundled with GROMACS." OFF)
 +mark_as_advanced(GMX_EXTERNAL_TINYXML2)
 +if(GMX_EXTERNAL_TINYXML2)
 +    # Find an external TinyXML-2 library.
 +    find_package(TinyXML-2 3.0.0)
 +    set(HAVE_TINYXML2 ${TinyXML2_FOUND})
 +    if(NOT HAVE_TINYXML2)
 +        message(FATAL_ERROR "External TinyXML-2 could not be found, please adjust your search paths")
 +    endif()
 +endif()
 +
  option(GMX_EXTRAE "Add support for tracing using EXTRAE" OFF)
  mark_as_advanced(GMX_EXTRAE)
  
@@@ -564,11 -489,12 +565,15 @@@ if (TMPI_ATOMICS_DISABLED
     add_definitions(-DTMPI_ATOMICS_DISABLED)
  endif()
  
- # now that we have detected the dependencies, do the second GPU configure pass
- gmx_gpu_setup()
 +# Note this relies on zlib detection having already run
 +include(gmxManageTNG)
 +
+ if(GMX_GPU)
+     # now that we have detected the dependencies, do the second configure pass
+     gmx_gpu_setup()
+ else()
+     mark_as_advanced(CUDA_HOST_COMPILER)
+ endif()
  
  if(CYGWIN)
      set(GMX_CYGWIN 1)
@@@ -590,9 -516,43 +595,9 @@@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin"
      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)
 -    if(DEFINED Boost_INCLUDE_DIR)
 -        set(Boost_FIND_QUIETLY TRUE)
 -    endif()
 -    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()
 -
 -if(NOT DEFINED GMX_BUILD_UNITTESTS AND NOT HAVE_LIBXML2)
 -    message(WARNING "libxml2 not found. Will build GROMACS without unit-tests. This is not recommended, because the unit-tests help to verify that GROMACS functions correctly. Most likely you are missing the libxml2-dev(el) package. After you installed it, set GMX_BUILD_UNITTESTS=ON.")
 -endif()
 -option(GMX_BUILD_UNITTESTS "Build unit tests with BUILD_TESTING (uses Google C++ Testing and Mocking Frameworks, requires libxml2)" ${HAVE_LIBXML2})
 +option(GMX_BUILD_UNITTESTS "Build unit tests with BUILD_TESTING" ON)
  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()
  
  ########################################################################
  # Our own GROMACS tests
@@@ -629,6 -589,11 +634,6 @@@ if(GMX_USE_RDTSCP
      set(HAVE_RDTSCP 1)
  endif()
  
 -include(gmxTestFloatFormat)
 -gmx_test_float_format(GMX_FLOAT_FORMAT_IEEE754
 -                      GMX_IEEE754_BIG_ENDIAN_BYTE_ORDER
 -                      GMX_IEEE754_BIG_ENDIAN_WORD_ORDER)
 -
  include(gmxTestLargeFiles)
  gmx_test_large_files(GMX_LARGEFILES)
  
@@@ -638,6 -603,30 +643,6 @@@ gmx_test_sigusr1(HAVE_SIGUSR1
  include(gmxTestPipes)
  gmx_test_pipes(HAVE_PIPES)
  
 -include(gmxTestIsfinite)
 -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)
 -# clang with some older versions of boost fail to work with C++11
 -if(CMAKE_CXX_COMPILER_ID MATCHES Clang AND
 -    (Boost_VERSION VERSION_EQUAL 104601 OR Boost_VERSION VERSION_EQUAL 104700))
 -    set(CLANG_AND_OLD_EXTERNAL_BOOST TRUE)
 -endif()
 -# 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 AND NOT CLANG_AND_OLD_EXTERNAL_BOOST" OFF)
 -mark_as_advanced(GMX_CXX11)
 -if(GMX_CXX11)
 -    set(EXTRA_CXX_FLAGS "${EXTRA_CXX_FLAGS} ${GMX_CXX11_FLAGS}")
 -endif()
 -
 -# Now we can test for CXX11_REGEX include file
  check_include_file_cxx(regex     HAVE_CXX11_REGEX)
  
  include(gmxTestXDR)
@@@ -860,13 -849,30 +865,13 @@@ endif(
  #Simpler to always install.
  install(FILES COPYING DESTINATION ${DATA_INSTALL_DIR} COMPONENT data)
  
 -if(GMX_EXTERNAL_BOOST)
 -    include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
 -    list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${Boost_INCLUDE_DIRS})
 -else()
 -    include_directories(BEFORE SYSTEM ${CMAKE_SOURCE_DIR}/src/external/boost)
 -    list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${INCL_INSTALL_DIR}/gromacs/external/boost)
 -    list(APPEND INSTALLED_HEADED_DEFINITIONS "-DBOOST_NO_TYPEID")
 -    # typeid not supported for minimal internal version
 -    # (would add significant amount of code)
 -    add_definitions(-DBOOST_NO_TYPEID)
 -    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_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)
 +    add_definitions(-DNDEBUG -DGMX_DISABLE_ASSERTS)
  endif()
  
  if (BUILD_TESTING)
index 4abf65e290faa90d884b89d2d79f2a9869156018,78225473f9cff55d83952edf939f93661b79a603..28fbdc21f63514e0b02692e0d08a4f482e0fc84d
  include(gmxTestInlineASM)
  
  function(gmx_suggest_simd _suggested_simd)
+     if(${_suggested_simd})
+         # There's already been a suggestion made, which can't change
+         return()
+     endif()
  
      # for x86 we need inline asm to use cpuid
      gmx_test_inline_asm_gcc_x86(GMX_X86_GCC_INLINE_ASM)
  
      if(GMX_X86_GCC_INLINE_ASM)
 -        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM")
 +        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM=1")
      else()
 -        set(GCC_INLINE_ASM_DEFINE "")
 +        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM=0")
      endif()
  
      message(STATUS "Detecting best SIMD instructions for this CPU")
  
      # Get CPU SIMD properties information
 -    set(_compile_definitions "${GCC_INLINE_ASM_DEFINE} -I${CMAKE_SOURCE_DIR}/src -DGMX_CPUID_STANDALONE")
 +    set(_compile_definitions "${GCC_INLINE_ASM_DEFINE} -I${CMAKE_SOURCE_DIR}/src -DGMX_CPUINFO_STANDALONE ${GMX_STDLIB_CXX_FLAGS}")
+     if(GMX_TARGET_X86)
+         set(_compile_definitions "${_compile_definitions} -DGMX_TARGET_X86")
+     endif()
+     # Prepare a default suggestion
+     set(OUTPUT_SIMD "None")
  
      # We need to execute the binary, so this only works if not cross-compiling.
      # However, note that we are NOT limited to x86.
      if(NOT CMAKE_CROSSCOMPILING)
-         # We can use try_run(... LINK_LIBRARIES ${GMX_STDLIB_LIBRARIES})
-         # once we require CMake at least 2.8.11. The code below works
-         # even when GMX_STDLIB_LIBRARIES is empty.
+         # TODO Extract this try_compile to a helper function, because
+         # it duplicates code in gmxDetectSimd.cmake
+         set(GMX_DETECTSIMD_BINARY "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/GmxDetectSimd${CMAKE_EXECUTABLE_SUFFIX}")
 +        set(LINK_LIBRARIES "${GMX_STDLIB_LIBRARIES}")
-         try_run(GMX_CPUINFO_RUN_SIMD GMX_CPUINFO_COMPILED
-                 ${CMAKE_BINARY_DIR}
-                 ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-                 COMPILE_DEFINITIONS ${_compile_definitions}
-                 CMAKE_FLAGS "-DLINK_LIBRARIES=${LINK_LIBRARIES}"
-                 RUN_OUTPUT_VARIABLE OUTPUT_TMP
-                 COMPILE_OUTPUT_VARIABLE GMX_CPUINFO_COMPILE_OUTPUT
-                 ARGS "-features")
-         if(NOT GMX_CPUINFO_COMPILED)
-             message(WARNING "Cannot compile cpuinfo code, which means no SIMD instructions.")
-             message(STATUS "Compile output: ${GMX_CPUINFO_COMPILE_OUTPUT}")
-             set(OUTPUT_TMP "None")
-         elseif(NOT GMX_CPUINFO_RUN_SIMD EQUAL 0)
-             message(WARNING "Cannot run cpuinfo code, which means no SIMD instructions.")
-             message(STATUS "Run output: ${OUTPUT_TMP}")
-             set(OUTPUT_TMP "None")
-         endif(NOT GMX_CPUINFO_COMPILED)
+         try_compile(GMX_DETECTSIMD_COMPILED
+             "${CMAKE_CURRENT_BINARY_DIR}"
 -            "${CMAKE_CURRENT_SOURCE_DIR}/src/gromacs/gmxlib/gmx_cpuid.c"
++            "${CMAKE_CURRENT_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp"
+             COMPILE_DEFINITIONS "${_compile_definitions}"
++            CMAKE_FLAGS "-DLINK_LIBRARIES=${LINK_LIBRARIES}"
+             OUTPUT_VARIABLE GMX_DETECTSIMD_COMPILED_OUTPUT
+             COPY_FILE ${GMX_DETECTSIMD_BINARY})
+         unset(_compile_definitions)
  
-         set(OUTPUT_SIMD "None")
-         if(GMX_TARGET_X86)
-             if(OUTPUT_TMP MATCHES " avx512er ")
-                 set(OUTPUT_SIMD "AVX_512_KNL")
-             elseif(OUTPUT_TMP MATCHES " avx512f ")
-                 set(OUTPUT_SIMD "AVX_512")
-             elseif(OUTPUT_TMP MATCHES " avx2 ")
-                 set(OUTPUT_SIMD "AVX2_256")
-             elseif(OUTPUT_TMP MATCHES " avx ")
-                 if(OUTPUT_TMP MATCHES " fma4 ")
-                     # AMD that works better with avx-128-fma
-                 set(OUTPUT_SIMD "AVX_128_FMA")
+         if(GMX_DETECTSIMD_COMPILED)
+             # TODO Extract this duplication of
+             # gmxSetBuildInformation.cmake to a helper function
+             if(NOT DEFINED GMX_DETECTSIMD_RUN)
+                 execute_process(COMMAND ${GMX_DETECTSIMD_BINARY} "-simd"
+                     RESULT_VARIABLE GMX_DETECTSIMD_RUN
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_DETECTSIMD_RUN "${GMX_DETECTSIMD_RUN}" CACHE INTERNAL "Result of running CPUID code with arg -simd")
+                 if(GMX_DETECTSIMD_RUN EQUAL 0)
+                     # Make a concrete suggestion of SIMD level
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_SIMD)
+                     message(STATUS "Detected best SIMD instructions for this CPU - ${OUTPUT_SIMD}")
                  else()
-                     # Intel
-                 set(OUTPUT_SIMD "AVX_256")
+                     message(WARNING "Cannot run CPUID code, which means no SIMD suggestion can be made.")
+                     message(STATUS "Run output: ${OUTPUT_TMP}")
                  endif()
-             elseif(OUTPUT_TMP MATCHES " sse4.1 ")
-                 set(OUTPUT_SIMD "SSE4.1")
-             elseif(OUTPUT_TMP MATCHES " sse2 ")
-                 set(OUTPUT_SIMD "SSE2")
              endif()
          else()
-             if(OUTPUT_TMP MATCHES " vsx ")
-                 set(OUTPUT_SIMD "IBM_VSX")
-             elseif(OUTPUT_TMP MATCHES " vmx ")
-                 set(OUTPUT_SIMD "IBM_VMX")
-             elseif(OUTPUT_TMP MATCHES " qpx ")
-                 set(OUTPUT_SIMD "IBM_QPX")
-             elseif(OUTPUT_TMP MATCHES " neon_asimd ")
-                 set(OUTPUT_SIMD "ARM_NEON_ASIMD")
-             elseif(OUTPUT_TMP MATCHES " neon ")
-                 set(OUTPUT_SIMD "ARM_NEON")
-             endif()
+             message(WARNING "Cannot compile CPUID code, which means no SIMD instructions.")
+             message(STATUS "Compile output: ${GMX_CPUID_COMPILE_OUTPUT}")
          endif()
-         set(${_suggested_simd} "${OUTPUT_SIMD}" PARENT_SCOPE)
-         message(STATUS "Detected best SIMD instructions for this CPU - ${OUTPUT_SIMD}")
      else()
-         set(${_suggested_simd} "None" PARENT_SCOPE)
          message(WARNING "Cannot detect SIMD architecture for this cross-compile; you should check it manually.")
      endif()
+     set(${_suggested_simd} "${OUTPUT_SIMD}" CACHE INTERNAL "Suggested SIMD")
  endfunction()
  
  function(gmx_detect_simd _suggested_simd)
index a0281a99181cb1beeaad8e0b7ed973c3ce93a588,028a83ff1c857dc9495157b78db4ea42b640b141..17ef7db1ed04c06a0fd2bc8f84019dbae35f4bbc
@@@ -1,7 -1,7 +1,7 @@@
  #
  # This file is part of the GROMACS molecular simulation package.
  #
 -# Copyright (c) 2012,2013,2014,215, by the GROMACS development team, led by
 +# Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team, led by
  # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  # and including many others, as listed in the AUTHORS file in the
  # top-level source directory and at http://www.gromacs.org.
@@@ -36,7 -36,7 +36,7 @@@
  # pain as much as possible:
  # - use the CUDA_HOST_COMPILER if defined by the user, otherwise
  # - auto-detect compatible nvcc host compiler and set nvcc -ccbin (if not MPI wrapper)
 -# - set icc compatibility mode to gcc 4.4/4.5 (CUDA 4.0 is not compatible with gcc >v4.4)
 +# - set icc compatibility mode to gcc 4.6
  # - (advanced) variables set:
  #   * CUDA_HOST_COMPILER            - the host compiler for nvcc (only with cmake <2.8.10)
  #   * CUDA_HOST_COMPILER_OPTIONS    - the full host-compiler related option list passed to nvcc
@@@ -51,7 -51,8 +51,7 @@@ if (CUDA_HOST_COMPILER_CHANGED AND CUDA
      unset(CUDA_HOST_COMPILER_AUTOSET CACHE)
  endif()
  
 -# Explicitly set the host compiler for nvcc if the current compiler is
 -# supported and it's not an MPI compiler wrapper, otherwise warn the user.
 +# Set the host compiler for nvcc if this is not set by CMake (v<=2.8.9)
  #
  # Note that even though nvcc compiles host code as C++, we use the
  # CMAKE_C_COMPILER as host compiler. We do this because CUDA versions
  # Also note that with MSVC nvcc sets the -compiler-bindir option behind the
  # scenes; to avoid conflicts we don't set -ccbin automatically.
  #
 -# This will be executed only with cmake <v2.8.10 as later versions set the
 -# host compiler in FindCUDA.
 +# TODO: remove this when CMAke >=v2.8.10 is required.
  if (NOT DEFINED CUDA_HOST_COMPILER AND NOT MSVC)
 -    if (NOT CMAKE_COMPILER_IS_GNUCC AND
 -        NOT (CMAKE_C_COMPILER_ID MATCHES "Intel" AND UNIX AND NOT APPLE))
 -        message(WARNING "
 -        Will not set the nvcc host compiler because the current C compiler is not
 -        compatible with nvcc:
 -        ${CMAKE_C_COMPILER} (ID: ${CMAKE_C_COMPILER_ID})
 -        Compatible compilers are: gcc on Linux and Mac OS X, the Intel Compiler on 64-bit
 -        Linux and MSVC on Windows. Note that with newer CUDA releases this might change,
 -        for up-to-date compatibility information check the NVIDIA documentation.
 -        If nothing specified, nvcc will automatically pick the platform-default compiler;
 -        Note that mixing compilers can cause errors.
 -        To manually set the nvcc host compiler, edit CUDA_NVCC_FLAGS or re-configure
 -        setting CUDA_HOST_COMPILER to the full path of a compatible compiler.
 -        ")
 -    else()
 -        # do not use MPI compiler wrappers, as these are prone to brake nvcc
 -        if (GMX_MPI AND NOT "${MPI_C_FOUND}") # FindMPI-based detection
 -            message(WARNING "
 -        Will not set the nvcc host compiler because the current C compiler is an MPI
 -        compiler wrapper: ${CMAKE_C_COMPILER}
 -        MPI compiler wrappers are prone to not work with nvcc. You might get lucky,
 -        but the safest is to use the C compiler that the MPI compiler wrapper uses
 -        (if this is compatible).
 -        To manually set the nvcc host compiler, edit CUDA_NVCC_FLAGS or re-configure
 -        setting CUDA_HOST_COMPILER to the full path of a compatible compiler.
 -        ")
 -        else()
 -            set(CUDA_HOST_COMPILER "${CMAKE_C_COMPILER}")
 -            set(CUDA_HOST_COMPILER_AUTOSET TRUE CACHE INTERNAL
 -                "True if CUDA_HOST_COMPILER is automatically set")
 -        endif()
 -    endif()
 +    set(CUDA_HOST_COMPILER "${CMAKE_C_COMPILER}")
 +    set(CUDA_HOST_COMPILER_AUTOSET TRUE CACHE INTERNAL
 +        "True if CUDA_HOST_COMPILER is automatically set")
  endif()
  
  # set up host compiler and its options
  if(CUDA_HOST_COMPILER_CHANGED)
      # FindCUDA in CMake 2.8.10 sets the host compiler internally
      if (CMAKE_VERSION VERSION_LESS "2.8.10")
 -        message(STATUS "Setting the nvcc host compiler to: ${CUDA_HOST_COMPILER}")
          set(CUDA_HOST_COMPILER ${CUDA_HOST_COMPILER}
              CACHE PATH "Host compiler for nvcc")
      endif()
  
 -    # On *nix force icc in gcc 4.4 compatibility mode with CUDA 3.2/4.0 and
 -    # gcc 4.5 compatibility mode with later CUDA versions. This is needed
 +    # On *nix force icc in gcc 4.6 compatibility mode. This is needed
      # as even with icc used as host compiler, when icc's gcc compatibility
      # mode is higher than the max gcc version officially supported by CUDA,
      # nvcc will freak out.
                (CUDA_HOST_COMPILER_AUTOSET OR CMAKE_C_COMPILER STREQUAL CUDA_HOST_COMPILER)) OR
              (CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND CMAKE_CXX_COMPILER STREQUAL CUDA_HOST_COMPILER))
          )
 -        if (CUDA_VERSION VERSION_LESS "4.1")
 -            message(STATUS "Setting Intel Compiler compatibity mode to gcc 4.4 for nvcc host compilation")
 -            list(APPEND CUDA_HOST_COMPILER_OPTIONS "-Xcompiler;-gcc-version=440")
 -        else()
 -            message(STATUS "Setting Intel Compiler compatibity mode to gcc 4.5 for nvcc host compilation")
 -            list(APPEND CUDA_HOST_COMPILER_OPTIONS "-Xcompiler;-gcc-version=450")
 -        endif()
 +        message(STATUS "Setting Intel Compiler compatibity mode to gcc 4.6 for nvcc host compilation")
 +        list(APPEND CUDA_HOST_COMPILER_OPTIONS "-Xcompiler;-gcc-version=460")
      endif()
  
      if(APPLE AND CMAKE_C_COMPILER_ID MATCHES "GNU")
      mark_as_advanced(CUDA_HOST_COMPILER CUDA_HOST_COMPILER_OPTIONS)
  endif()
  
 -# the legacy CUDA kernels have been dropped, warn with CUDA 4.0
 -if (CUDA_VERSION VERSION_EQUAL "4.0")
 -    message(WARNING "The legacy GPU kernels optimized for older CUDA compilers, including the detected version 4.0, have been removed. To avoid performance loss, we strongly recommend upgrading to a newer CUDA toolkit.
 -    ")
 -endif()
 -
  # If any of these manual override variables for target CUDA GPU architectures
  # or virtual architecture is set, parse the values and assemble the nvcc
  # command line for these. Otherwise use our defaults.
@@@ -119,12 -163,19 +119,15 @@@ if (GMX_CUDA_TARGET_SM OR GMX_CUDA_TARG
      endforeach()
  else()
      # Set the CUDA GPU architectures to compile for:
 -    # - with CUDA  <4.2:        compute capability 2.x supported (compiling for sm_2.1 does not help):
 -    #     => compile sm_20 SASS, and compute_20 PTX
 -    # - with CUDA  =4.2 <5.0:   CC <=3.0 is supported:
 -    #     => compile sm_20, sm_30 SASS, and compute_30 PTX
      # - with CUDA >=5.0 <6.5:   CC <=3.5 is supported
      #     => compile sm_20, sm_30, sm_35 SASS, and compute_35 PTX
      # - with CUDA ==6.5:        CC <=3.7 and 5.0 are supported
      #     => compile sm_20, sm_30, sm_35, sm_37 sm_50, SASS, and compute_50 PTX
      # - with CUDA >=7.0         CC 5.2 is supported (5.3, Tegra X1 we don't generate code for)
      #     => compile sm_20, sm_30, sm_35, sm_37, sm_50, & sm_52 SASS, and compute_52 PTX
+     # - with CUDA >=8.0         CC 6.0-6.2 is supported (but we know nothing about CC 6.2, so we won't generate code or it)
+     #     => compile sm_20, sm_30, sm_35, sm_37, sm_50, sm_52, sm_60, sm_61 SASS, and compute_60 and compute_61 PTX
+     #
      #
      #   Note that CUDA 6.5.19 second patch release supports cc 5.2 too, but
      #   CUDA_VERSION does not contain patch version and having PTX 5.0 JIT-ed is
  
      # First add flags that trigger SASS (binary) code generation for physical arch
      list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_20,code=sm_20")
 +    list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_30,code=sm_30")
 +    list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_35,code=sm_35")
  
 -    if(NOT CUDA_VERSION VERSION_LESS "4.2") # >= 4.2
 -        list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_30,code=sm_30")
 -    endif()
 -    if(NOT CUDA_VERSION VERSION_LESS "5.0") # >= 5.0
 -        list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_35,code=sm_35")
 -    endif()
      if(NOT CUDA_VERSION VERSION_LESS "6.5") # >= 6.5
          list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_37,code=sm_37")
          list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_50,code=sm_50")
      if(NOT CUDA_VERSION VERSION_LESS "7.0") # >= 7.0
          list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_52,code=sm_52")
      endif()
+     if(NOT CUDA_VERSION VERSION_LESS "8.0") # >= 8.0
+         list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_60,code=sm_60")
+         list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_61,code=sm_61")
+     endif()
  
      # Next add flags that trigger PTX code generation for the newest supported virtual arch
      # that's useful to JIT to future architectures
 -    if(CUDA_VERSION VERSION_LESS "4.2")
 -        list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_20,code=compute_20")
 -    elseif(CUDA_VERSION VERSION_LESS "5.0")
 -        list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_30,code=compute_30")
 -    elseif(CUDA_VERSION VERSION_LESS "6.5")
 +    if(CUDA_VERSION VERSION_LESS "6.5")
          list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_35,code=compute_35")
      elseif(CUDA_VERSION VERSION_LESS "7.0")
          list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_50,code=compute_50")
-     else() # version >= 7.0
+     elseif(CUDA_VERSION VERSION_LESS "8.0")
          list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_52,code=compute_52")
+     else() # version >= 8.0
+         list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_60,code=compute_60")
+         list (APPEND GMX_CUDA_NVCC_GENCODE_FLAGS "-gencode;arch=compute_61,code=compute_61")
      endif()
  endif()
  
@@@ -168,46 -234,5 +178,46 @@@ if (CMAKE_VERSION VERSION_LESS "2.8.10"
  endif()
  list(APPEND GMX_CUDA_NVCC_FLAGS "${CUDA_HOST_COMPILER_OPTIONS}")
  
 -# finally set the damn flags
 -set(CUDA_NVCC_FLAGS "${GMX_CUDA_NVCC_FLAGS}" CACHE STRING "Compiler flags for nvcc." FORCE)
 +# The flags are set as local variables which shadow the cache variables. The cache variables
 +# (can be set by the user) are appended. This is done in a macro to set the flags when all
 +# host compiler flags are already set.
 +macro(GMX_SET_CUDA_NVCC_FLAGS)
 +    if(CUDA_PROPAGATE_HOST_FLAGS)
 +        set(CUDA_PROPAGATE_HOST_FLAGS OFF)
 +
 +        # When CUDA 6.5 is required we should use C++11 also for CUDA and also propagate
 +        # the C++11 flag to CUDA. Then we can use the solution implemented in FindCUDA
 +        # (starting with 3.3 - can be backported). For now we need to remove the C++11
 +        # flag which means we need to manually propagate all other flags.
 +        string(REGEX REPLACE "[-]+std=c\\+\\+0x" "" _CMAKE_CXX_FLAGS_SANITIZED "${CMAKE_CXX_FLAGS}")
 +
 +        # The IBM xlc compiler chokes if we use both altivec and Cuda. Solve
 +        # this by not propagating the flag in this case.
 +        if(CMAKE_CXX_COMPILER_ID MATCHES "XL")
 +            string(REGEX REPLACE "-qaltivec" "" _CMAKE_CXX_FLAGS_SANITIZED "${_CMAKE_CXX_FLAGS_SANITIZED}")
 +        endif()
 +
 +        # CUDA versions prior to 7.5 come with a header (math_functions.h) which uses the _MSC_VER macro
 +        # unconditionally, so we strip -Wundef from the propagatest flags for earlier CUDA versions.
 +        if (CUDA_VERSION VERSION_LESS "7.5")
 +            string(REGEX REPLACE "-Wundef" "" _CMAKE_CXX_FLAGS_SANITIZED "${_CMAKE_CXX_FLAGS_SANITIZED}")
 +        endif()
 +
 +        string(REPLACE " " "," _flags "${_CMAKE_CXX_FLAGS_SANITIZED}")
 +        set(CUDA_NVCC_FLAGS "${GMX_CUDA_NVCC_FLAGS};${CUDA_NVCC_FLAGS};-Xcompiler;${_flags}")
 +
 +        # Create list of all possible configurations. For multi-configuration this is CMAKE_CONFIGURATION_TYPES
 +        # and for single configuration CMAKE_BUILD_TYPE. Not sure why to add the default ones, but FindCUDA
 +        # claims one should.
 +        set(CUDA_configuration_types ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE} Debug MinSizeRel Release RelWithDebInfo)
 +        list(REMOVE_DUPLICATES CUDA_configuration_types)
 +
 +        foreach(_config ${CUDA_configuration_types})
 +            string(TOUPPER ${_config} _config_upper)
 +            string(REPLACE " " "," _flags "${CMAKE_CXX_FLAGS_${_config_upper}}")
 +            set(CUDA_NVCC_FLAGS_${_config_upper} "${CUDA_NVCC_FLAGS_${_config_upper}};-Xcompiler;${_flags}")
 +        endforeach()
 +    else()
 +        set(CUDA_NVCC_FLAGS "${GMX_CUDA_NVCC_FLAGS};${CUDA_NVCC_FLAGS}")
 +    endif()
 +endmacro()
index bf6551f9e824e7b4a75fc81d74697a913bf9cb56,c53f369d5ba30e90dbff472784852ecc64a1099f..e3d677ac57d191ca602ad456e88017d7cbce4d5f
@@@ -1,7 -1,7 +1,7 @@@
  #
  # This file is part of the GROMACS molecular simulation package.
  #
 -# Copyright (c) 2012,2013,2014,2016, by the GROMACS development team, led by
 +# Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team, led by
  # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  # and including many others, as listed in the AUTHORS file in the
  # top-level source directory and at http://www.gromacs.org.
@@@ -59,9 -59,9 +59,9 @@@ macro(gmx_set_build_information
      gmx_test_inline_asm_gcc_x86(GMX_X86_GCC_INLINE_ASM)
  
      if(GMX_X86_GCC_INLINE_ASM)
 -        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM")
 +        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM=1")
      else()
 -        set(GCC_INLINE_ASM_DEFINE "")
 +        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM=0")
      endif()
  
      message(STATUS "Setting build user/date/host/cpu information")
          message(STATUS "Setting build user & time - not on Unix, using anonymous")
      endif()
  
-     if(NOT CMAKE_CROSSCOMPILING)
-         # Get CPU information, e.g. for deciding what SIMD support exists
-         set(_compile_definitions "${GCC_INLINE_ASM_DEFINE} -I${CMAKE_SOURCE_DIR}/src -DGMX_CPUINFO_STANDALONE")
-         try_run(GMX_CPUINFO_RUN_VENDOR GMX_CPUINFO_COMPILED
-             ${CMAKE_BINARY_DIR}
-             ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-             COMPILE_DEFINITIONS ${_compile_definitions}
-             RUN_OUTPUT_VARIABLE OUTPUT_CPU_VENDOR ARGS "-vendor")
-         try_run(GMX_CPUINFO_RUN_BRAND GMX_CPUINFO_COMPILED
-             ${CMAKE_BINARY_DIR}
-             ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-             COMPILE_DEFINITIONS ${_compile_definitions}
-             RUN_OUTPUT_VARIABLE OUTPUT_CPU_BRAND ARGS "-brand")
-         try_run(GMX_CPUINFO_RUN_FAMILY GMX_CPUINFO_COMPILED
-             ${CMAKE_BINARY_DIR}
-             ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-             COMPILE_DEFINITIONS ${_compile_definitions}
-             RUN_OUTPUT_VARIABLE OUTPUT_CPU_FAMILY ARGS "-family")
-         try_run(GMX_CPUINFO_RUN_MODEL GMX_CPUINFO_COMPILED
-             ${CMAKE_BINARY_DIR}
-             ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-             COMPILE_DEFINITIONS ${_compile_definitions}
-             RUN_OUTPUT_VARIABLE OUTPUT_CPU_MODEL ARGS "-model")
-        try_run(GMX_CPUINFO_RUN_STEPPING GMX_CPUINFO_COMPILED
-             ${CMAKE_BINARY_DIR}
-             ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-             COMPILE_DEFINITIONS ${_compile_definitions}
-             RUN_OUTPUT_VARIABLE OUTPUT_CPU_STEPPING ARGS "-stepping")
-         try_run(GMX_CPUINFO_RUN_FEATURES GMX_CPUINFO_COMPILED
-             ${CMAKE_BINARY_DIR}
-             ${CMAKE_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp
-             COMPILE_DEFINITIONS ${_compile_definitions}
-             RUN_OUTPUT_VARIABLE OUTPUT_CPU_FEATURES ARGS "-features")
-         unset(_compile_definitions)
-         string(STRIP "${OUTPUT_CPU_VENDOR}" OUTPUT_CPU_VENDOR)
-         string(STRIP "${OUTPUT_CPU_BRAND}" OUTPUT_CPU_BRAND)
-         string(STRIP "${OUTPUT_CPU_FAMILY}" OUTPUT_CPU_FAMILY)
-         string(STRIP "${OUTPUT_CPU_MODEL}" OUTPUT_CPU_MODEL)
-         string(STRIP "${OUTPUT_CPU_STEPPING}" OUTPUT_CPU_STEPPING)
-         string(STRIP "${OUTPUT_CPU_FEATURES}" OUTPUT_CPU_FEATURES)
+     # Set up some defaults that will usually be overridden
+     if(CMAKE_CROSSCOMPILING)
+         set(_reason ", cross-compiled")
+     endif()
+     set(OUTPUT_CPU_VENDOR   "Unknown${_reason}")
+     set(OUTPUT_CPU_BRAND    "Unknown${_reason}")
+     set(OUTPUT_CPU_FAMILY   "0")
+     set(OUTPUT_CPU_MODEL    "0")
+     set(OUTPUT_CPU_STEPPING "0")
+     set(OUTPUT_CPU_FEATURES "Unknown${_reason}")
+     unset(_reason)
  
-         if(GMX_CPUINFO_RUN_VENDOR EQUAL 0)
-             set(BUILD_CPU_VENDOR   "${OUTPUT_CPU_VENDOR}"   CACHE INTERNAL "Build CPU vendor")
-         else()
-             set(BUILD_CPU_VENDOR   "Unknown, detect failed" CACHE INTERNAL "Build CPU vendor")
-         endif()
-         if(GMX_CPUINFO_RUN_BRAND EQUAL 0)
-             set(BUILD_CPU_BRAND    "${OUTPUT_CPU_BRAND}"    CACHE INTERNAL "Build CPU brand")
-         else()
-             set(BUILD_CPU_BRAND    "Unknown, detect failed" CACHE INTERNAL "Build CPU brand")
-         endif()
-         if(GMX_CPUINFO_RUN_FAMILY EQUAL 0)
-             set(BUILD_CPU_FAMILY   "${OUTPUT_CPU_FAMILY}"   CACHE INTERNAL "Build CPU family")
-         else()
-             set(BUILD_CPU_FAMILY   "0"                     CACHE INTERNAL "Build CPU family")
-         endif()
-         if(GMX_CPUINFO_RUN_MODEL EQUAL 0)
-             set(BUILD_CPU_MODEL    "${OUTPUT_CPU_MODEL}"    CACHE INTERNAL "Build CPU model")
-         else()
-             set(BUILD_CPU_MODEL    "0"                     CACHE INTERNAL "Build CPU model")
-         endif()
-         if(GMX_CPUINFO_RUN_STEPPING EQUAL 0)
-             set(BUILD_CPU_STEPPING "${OUTPUT_CPU_STEPPING}" CACHE INTERNAL "Build CPU stepping")
-         else()
-             set(BUILD_CPU_STEPPING "0"                     CACHE INTERNAL "Build CPU stepping")
-         endif()
-             if(GMX_CPUINFO_RUN_FEATURES EQUAL 0)
-             set(BUILD_CPU_FEATURES "${OUTPUT_CPU_FEATURES}" CACHE INTERNAL "Build CPU features")
-         else()
-             set(BUILD_CPU_FEATURES ""                      CACHE INTERNAL "Build CPU features")
+     if(NOT CMAKE_CROSSCOMPILING)
+         # Get CPU information, e.g. for deciding what SIMD support probably exists
+         set(_compile_definitions "${GCC_INLINE_ASM_DEFINE} -I${CMAKE_SOURCE_DIR}/src -DGMX_CPUID_STANDALONE")
+         if(GMX_TARGET_X86)
+             set(_compile_definitions "${_compile_definitions} -DGMX_TARGET_X86")
          endif()
  
-     else()
-         set(BUILD_CPU_VENDOR   "Unknown, cross-compiled"   CACHE INTERNAL "Build CPU vendor")
-         set(BUILD_CPU_BRAND    "Unknown, cross-compiled"    CACHE INTERNAL "Build CPU brand")
-         set(BUILD_CPU_FAMILY   "0"   CACHE INTERNAL "Build CPU family")
-         set(BUILD_CPU_MODEL    "0"    CACHE INTERNAL "Build CPU model")
-         set(BUILD_CPU_STEPPING "0" CACHE INTERNAL "Build CPU stepping")
-         set(BUILD_CPU_FEATURES "" CACHE INTERNAL "Build CPU features")
+         set(GMX_BUILDINFORMATION_BINARY "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/GmxBuildInformation${CMAKE_EXECUTABLE_SUFFIX}")
+         # TODO Extract this try_compile to a helper function, because
+         # it duplicates code in gmxDetectSimd.cmake
+         try_compile(GMX_BUILDINFORMATION_COMPILED
+             "${CMAKE_CURRENT_BINARY_DIR}"
 -            "${CMAKE_CURRENT_SOURCE_DIR}/src/gromacs/gmxlib/gmx_cpuid.c"
++            "${CMAKE_CURRENT_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp"
+             COMPILE_DEFINITIONS "${_compile_definitions}"
+             OUTPUT_VARIABLE GMX_BUILDINFORMATION_COMPILED_OUTPUT
+             COPY_FILE ${GMX_BUILDINFORMATION_BINARY})
+         unset(_compile_definitions)
  
+         if(GMX_BUILDINFORMATION_COMPILED)
+             # TODO Extract this duplication to a helper function (also
+             # from gmxDetectSimd.cmake)
+             if(NOT DEFINED GMX_BUILDINFORMATION_RUN_VENDOR)
+                 execute_process(COMMAND ${GMX_BUILDINFORMATION_BINARY} "-vendor"
+                     RESULT_VARIABLE GMX_BUILDINFORMATION_RUN_VENDOR
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_BUILDINFORMATION_RUN_VENDOR "${GMX_BUILDINFORMATION_RUN_VENDOR}" CACHE INTERNAL "Result of running CPUID code with arg -vendor")
+                 if(GMX_BUILDINFORMATION_RUN_VENDOR EQUAL 0)
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_CPU_VENDOR)
+                 endif()
+             endif()
+             if(NOT DEFINED GMX_BUILDINFORMATION_RUN_BRAND)
+                 execute_process(COMMAND ${GMX_BUILDINFORMATION_BINARY} "-brand"
+                     RESULT_VARIABLE GMX_BUILDINFORMATION_RUN_BRAND
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_BUILDINFORMATION_RUN_BRAND "${GMX_BUILDINFORMATION_RUN_BRAND}" CACHE INTERNAL "Result of running CPUID code with arg -brand")
+                 if(GMX_BUILDINFORMATION_RUN_BRAND EQUAL 0)
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_CPU_BRAND)
+                 endif()
+             endif()
+             if(NOT DEFINED GMX_BUILDINFORMATION_RUN_FAMILY)
+                 execute_process(COMMAND ${GMX_BUILDINFORMATION_BINARY} "-family"
+                     RESULT_VARIABLE GMX_BUILDINFORMATION_RUN_FAMILY
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_BUILDINFORMATION_RUN_FAMILY "${GMX_BUILDINFORMATION_RUN_FAMILY}" CACHE INTERNAL "Result of running CPUID code with arg -family")
+                 if(GMX_BUILDINFORMATION_RUN_FAMILY EQUAL 0)
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_CPU_FAMILY)
+                 endif()
+             endif()
+             if(NOT DEFINED GMX_BUILDINFORMATION_RUN_MODEL)
+                 execute_process(COMMAND ${GMX_BUILDINFORMATION_BINARY} "-model"
+                     RESULT_VARIABLE GMX_BUILDINFORMATION_RUN_MODEL
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_BUILDINFORMATION_RUN_MODEL "${GMX_BUILDINFORMATION_RUN_MODEL}" CACHE INTERNAL "Result of running CPUID code with arg -model")
+                 if(GMX_BUILDINFORMATION_RUN_MODEL EQUAL 0)
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_CPU_MODEL)
+                 endif()
+             endif()
+             if(NOT DEFINED GMX_BUILDINFORMATION_RUN_STEPPING)
+                 execute_process(COMMAND ${GMX_BUILDINFORMATION_BINARY} "-stepping"
+                     RESULT_VARIABLE GMX_BUILDINFORMATION_RUN_STEPPING
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_BUILDINFORMATION_RUN_STEPPING "${GMX_BUILDINFORMATION_RUN_STEPPING}" CACHE INTERNAL "Result of running CPUID code with arg -stepping")
+                 if(GMX_BUILDINFORMATION_RUN_STEPPING EQUAL 0)
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_CPU_STEPPING)
+                 endif()
+             endif()
+             if(NOT DEFINED GMX_BUILDINFORMATION_RUN_FEATURES)
+                 execute_process(COMMAND ${GMX_BUILDINFORMATION_BINARY} "-features"
+                     RESULT_VARIABLE GMX_BUILDINFORMATION_RUN_FEATURES
+                     OUTPUT_VARIABLE OUTPUT_TMP
+                     ERROR_QUIET)
+                 set(GMX_BUILDINFORMATION_RUN_FEATURES "${GMX_BUILDINFORMATION_RUN_FEATURES}" CACHE INTERNAL "Result of running CPUID code with arg -features")
+                 if(GMX_BUILDINFORMATION_RUN_FEATURES EQUAL 0)
+                     string(STRIP "${OUTPUT_TMP}" OUTPUT_CPU_FEATURES)
+                 endif()
+             endif()
+         endif()
      endif()
  
+     set(BUILD_CPU_VENDOR   "${OUTPUT_CPU_VENDOR}"   CACHE INTERNAL "Build CPU vendor")
+     set(BUILD_CPU_BRAND    "${OUTPUT_CPU_BRAND}"    CACHE INTERNAL "Build CPU brand")
+     set(BUILD_CPU_FAMILY   "${OUTPUT_CPU_FAMILY}"   CACHE INTERNAL "Build CPU family")
+     set(BUILD_CPU_MODEL    "${OUTPUT_CPU_MODEL}"    CACHE INTERNAL "Build CPU model")
+     set(BUILD_CPU_STEPPING "${OUTPUT_CPU_STEPPING}" CACHE INTERNAL "Build CPU stepping")
+     set(BUILD_CPU_FEATURES "${OUTPUT_CPU_FEATURES}" CACHE INTERNAL "Build CPU features")
      ENDIF(NOT DEFINED BUILD_USER)
  endmacro(gmx_set_build_information)
index 83a68e02ec37215ee41a0c8bfbd880a905c9be9b,b5659d1d34908438745c598cb687ab19c2f53231..7f8f922d83c5f36ff0869828545a704f694256ec
@@@ -973,13 -973,13 +973,13 @@@ and one that does not (type 9)
  For details see \tabref{topfile2}.
  The table files are supplied to the {\tt mdrun} program.
  After the table file name an underscore, the letter ``b'' for bonds,
- ``a'' for angles or ``d'' for dihedrals and the table number are appended.
- For example, for a bond with $n=0$ (and using the default table file name)
the table is read from the file {\tt table_b0.xvg}.  Multiple tables can be
- supplied simply by using different values of $n$, and are applied to the appropriate
+ ``a'' for angles or ``d'' for dihedrals and the table number must be appended.
+ For example, a tabulated bond with $n=0$ can be read from the file {\tt table_b0.xvg}.
+ Multiple tables can be
+ supplied simply by adding files with different values of $n$, and are applied to the appropriate
  bonds, as specified in the topology (\tabref{topfile2}).
- The format for the table files is three columns with $x$, $f(x)$, $-f'(x)$,
where $x$ should be uniformly-spaced. Requirements for entries in the topology
+ The format for the table files is three fixed-format columns of any suitable width. These columns must contain $x$, $f(x)$, $-f'(x)$,
and the values of $x$ should be uniformly spaced. Requirements for entries in the topology
  are given in~\tabref{topfile2}. 
  The setup of the tables is as follows:
  \\{\bf bonds}:
@@@ -2030,13 -2030,48 +2030,13 @@@ type selectors (termed {\tt vdwtype} an
  parameters, for a total of six non-bonded interaction parameters. See
  the User Guide for a complete description of these parameters.
  
 -The neighbor searching (NS) can be performed using a single-range, or a twin-range 
 -approach. Since the former is merely a special case of the latter, we will 
 -discuss the more general twin-range. In this case, NS is described by two
 -radii: {\tt rlist} and max({\tt rcoulomb},{\tt rvdw}).
 -Usually one builds the neighbor list every 10 time steps
 -or every 20 fs (parameter {\tt nstlist}). In the neighbor list, all interaction 
 -pairs that  fall within {\tt rlist} are stored. Furthermore, the 
 -interactions between pairs that do not
 -fall within {\tt rlist} but do fall within max({\tt rcoulomb},{\tt rvdw})
 -are computed during NS.  The
 -forces and energy are stored separately and added to short-range forces
 -at every time step between successive NS. If {\tt rlist} = 
 -max({\tt rcoulomb},{\tt rvdw}), no forces
 -are evaluated during neighbor list generation.
 -The \normindex{virial} is calculated from the sum of the short- and
 -long-range forces.
 -This means that the virial can be slightly asymmetrical at non-NS steps.
 -When mdrun is compiled to use mixed precision, the virial is almost always asymmetrical because the
 -off-diagonal elements are about as large as each element in the sum.
 -In most cases this is not really a problem, since the fluctuations in the
 -virial can be 2 orders of magnitude larger than the average.
 -
 -Except for the plain cut-off,
 -all of the interaction functions in \tabref{funcparm}
 -require that neighbor searching be done with a larger radius than the $r_c$
 +In the group cut-off scheme, all of the interaction functions in \tabref{funcparm}
 +require that neighbor searching be done with a radius at least as large as the $r_c$
  specified for the functional form, because of the use of charge groups.
  The extra radius is typically of the order of 0.25 nm (roughly the 
  largest distance between two atoms in a charge group plus the distance a 
  charge group can diffuse within neighbor list updates).
  
 -%If your charge groups are very large it may be interesting to turn off charge
 -%groups, by setting the option 
 -%{\tt bAtomList = yes} in your {\tt grompp.mdp} file.
 -%In this case only a small extra radius to account for diffusion needs to be 
 -%added (0.1 nm). Do not however use this together with the plain cut-off
 -%method, as it will generate large artifacts (\secref{cg}).
 -%In summary, there are four parameters that describe NS behavior:
 -%{\tt nstlist} (update frequency in number of time steps),
 -%{\tt bAtomList} (whether or not charge groups are used to generate neighbor list, the default is to use charge groups, so {\tt bAtomList = no}),
 -%{\tt rshort} and {\tt rlong} which are the two radii {\rs} and {\rl}
 -%described above.
 -
  \begin{table}[ht]
  \centering
  \begin{tabular}{|ll|l|}
@@@ -2883,8 -2918,6 +2883,8 @@@ When selecting the CHARMM force field i
  
  A port of the CHARMM36 force field for use with GROMACS is also available at \url{http://mackerell.umaryland.edu/charmm_ff.shtml#gromacs}.
  
 +For branched polymers or other topologies not supported by {\tt \normindex{pdb2gmx}}, it is possible to use TopoTools~\cite{kohlmeyer2016} to generate a {\gromacs} top file.
 +
  \subsection{Coarse-grained force fields}
  \index{force-field, coarse-grained}
  \label{sec:cg-forcefields}
index b7f7d8b5a06e8e6c962ab8f69dbfe422880f96f4,0000000000000000000000000000000000000000..f8b5e55282807846ec925b42e2ab0150194ee19b
mode 100644,000000..100644
--- /dev/null
@@@ -1,291 -1,0 +1,306 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "filenm.h"
 +
 +#include <cstdio>
 +#include <cstring>
 +
 +#include "gromacs/fileio/filetypes.h"
 +#include "gromacs/utility/basedefinitions.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +/* Use bitflag ... */
 +#define IS_SET(fn) ((fn.flag &ffSET) != 0)
 +#define IS_OPT(fn) ((fn.flag &ffOPT) != 0)
 +
++const t_filenm *getFilenm(const char *opt, int nfile, const t_filenm fnm[])
++{
++    int i;
++
++    for (i = 0; (i < nfile); i++)
++    {
++        if (strcmp(opt, fnm[i].opt) == 0)
++        {
++            return &fnm[i];
++        }
++    }
++
++    return NULL;
++}
++
 +const char *opt2fn(const char *opt, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (std::strcmp(opt, fnm[i].opt) == 0)
 +        {
 +            return fnm[i].fns[0];
 +        }
 +    }
 +
 +    fprintf(stderr, "No option %s\n", opt);
 +
 +    return NULL;
 +}
 +
 +int opt2fns(char **fns[], const char *opt, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (strcmp(opt, fnm[i].opt) == 0)
 +        {
 +            *fns = fnm[i].fns;
 +            return fnm[i].nfiles;
 +        }
 +    }
 +
 +    fprintf(stderr, "No option %s\n", opt);
 +    return 0;
 +}
 +
 +const char *ftp2fn(int ftp, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (ftp == fnm[i].ftp)
 +        {
 +            return fnm[i].fns[0];
 +        }
 +    }
 +
 +    fprintf(stderr, "ftp2fn: No filetype %s\n", ftp2ext_with_dot(ftp));
 +    return NULL;
 +}
 +
 +int ftp2fns(char **fns[], int ftp, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (ftp == fnm[i].ftp)
 +        {
 +            *fns = fnm[i].fns;
 +            return fnm[i].nfiles;
 +        }
 +    }
 +
 +    fprintf(stderr, "ftp2fn: No filetype %s\n", ftp2ext_with_dot(ftp));
 +    return 0;
 +}
 +
 +gmx_bool ftp2bSet(int ftp, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (ftp == fnm[i].ftp)
 +        {
 +            return (gmx_bool) IS_SET(fnm[i]);
 +        }
 +    }
 +
 +    fprintf(stderr, "ftp2fn: No filetype %s\n", ftp2ext_with_dot(ftp));
 +
 +    return FALSE;
 +}
 +
 +gmx_bool opt2bSet(const char *opt, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (std::strcmp(opt, fnm[i].opt) == 0)
 +        {
 +            return (gmx_bool) IS_SET(fnm[i]);
 +        }
 +    }
 +
 +    fprintf(stderr, "No option %s\n", opt);
 +
 +    return FALSE;
 +}
 +
 +const char *opt2fn_null(const char *opt, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (std::strcmp(opt, fnm[i].opt) == 0)
 +        {
 +            if (IS_OPT(fnm[i]) && !IS_SET(fnm[i]))
 +            {
 +                return NULL;
 +            }
 +            else
 +            {
 +                return fnm[i].fns[0];
 +            }
 +        }
 +    }
 +    fprintf(stderr, "No option %s\n", opt);
 +    return NULL;
 +}
 +
 +const char *ftp2fn_null(int ftp, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nfile); i++)
 +    {
 +        if (ftp == fnm[i].ftp)
 +        {
 +            if (IS_OPT(fnm[i]) && !IS_SET(fnm[i]))
 +            {
 +                return NULL;
 +            }
 +            else
 +            {
 +                return fnm[i].fns[0];
 +            }
 +        }
 +    }
 +    fprintf(stderr, "ftp2fn: No filetype %s\n", ftp2ext_with_dot(ftp));
 +    return NULL;
 +}
 +
 +gmx_bool is_optional(const t_filenm *fnm)
 +{
 +    return ((fnm->flag & ffOPT) == ffOPT);
 +}
 +
 +gmx_bool is_output(const t_filenm *fnm)
 +{
 +    return ((fnm->flag & ffWRITE) == ffWRITE);
 +}
 +
 +gmx_bool is_set(const t_filenm *fnm)
 +{
 +    return ((fnm->flag & ffSET) == ffSET);
 +}
 +
 +int add_suffix_to_output_names(t_filenm *fnm, int nfile, const char *suffix)
 +{
 +    int   i, j;
 +    char  buf[STRLEN], newname[STRLEN];
 +    char *extpos;
 +
 +    for (i = 0; i < nfile; i++)
 +    {
 +        if (is_output(&fnm[i]) && fnm[i].ftp != efCPT)
 +        {
 +            /* We never use multiple _outputs_, but we might as well check
 +               for it, just in case... */
 +            for (j = 0; j < fnm[i].nfiles; j++)
 +            {
 +                std::strncpy(buf, fnm[i].fns[j], STRLEN - 1);
 +                extpos  = strrchr(buf, '.');
 +                *extpos = '\0';
 +                sprintf(newname, "%s%s.%s", buf, suffix, extpos + 1);
 +                sfree(fnm[i].fns[j]);
 +                fnm[i].fns[j] = gmx_strdup(newname);
 +            }
 +        }
 +    }
 +    return 0;
 +}
 +
 +t_filenm *dup_tfn(int nf, const t_filenm tfn[])
 +{
 +    int       i, j;
 +    t_filenm *ret;
 +
 +    snew(ret, nf);
 +    for (i = 0; i < nf; i++)
 +    {
 +        ret[i] = tfn[i]; /* just directly copy all non-string fields */
 +        if (tfn[i].opt)
 +        {
 +            ret[i].opt = gmx_strdup(tfn[i].opt);
 +        }
 +        else
 +        {
 +            ret[i].opt = NULL;
 +        }
 +
 +        if (tfn[i].fn)
 +        {
 +            ret[i].fn = gmx_strdup(tfn[i].fn);
 +        }
 +        else
 +        {
 +            ret[i].fn = NULL;
 +        }
 +
 +        if (tfn[i].nfiles > 0)
 +        {
 +            snew(ret[i].fns, tfn[i].nfiles);
 +            for (j = 0; j < tfn[i].nfiles; j++)
 +            {
 +                ret[i].fns[j] = gmx_strdup(tfn[i].fns[j]);
 +            }
 +        }
 +    }
 +    return ret;
 +}
 +
 +void done_filenms(int nf, t_filenm fnm[])
 +{
 +    int i, j;
 +
 +    for (i = 0; i < nf; ++i)
 +    {
 +        for (j = 0; j < fnm[i].nfiles; ++j)
 +        {
 +            sfree(fnm[i].fns[j]);
 +        }
 +        sfree(fnm[i].fns);
 +        fnm[i].fns = NULL;
 +    }
 +}
index 19c8f744de139c048f8540d78fc318df216a4fff,0000000000000000000000000000000000000000..1ca8470663085dac0d7b56f5a09ddad402d11221
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,185 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +/*! \file
 + * \brief
 + * Declares t_filenm for old-style command-line parsing of file name options.
 + *
 + * \inpublicapi
 + * \ingroup module_commandline
 + */
 +#ifndef GMX_COMMANDLINE_FILENM_H
 +#define GMX_COMMANDLINE_FILENM_H
 +
 +#include "gromacs/fileio/filetypes.h"
 +#include "gromacs/utility/basedefinitions.h"
 +
 +struct t_commrec;
 +
 +//! \addtogroup module_commandline
 +//! \{
 +
 +/*! \brief
 + * File name option definition for C code.
 + *
 + * \inpublicapi
 + */
 +struct t_filenm {
 +    int           ftp;    //!< File type (see enum in filetypes.h)
 +    const char   *opt;    //!< Command line option
 +    const char   *fn;     //!< File name (as set in source code)
 +    unsigned long flag;   //!< Flag for all kinds of info (see defs)
 +    int           nfiles; //!< number of files
 +    char        **fns;    //!< File names
 +};
 +
 +//! Whether a file name option is set.
 +#define ffSET   1<<0
 +//! Whether a file name option specifies an input file.
 +#define ffREAD  1<<1
 +//! Whether a file name option specifies an output file.
 +#define ffWRITE 1<<2
 +//! Whether a file name option specifies an optional file.
 +#define ffOPT   1<<3
 +//! Whether a file name option specifies a library file.
 +#define ffLIB   1<<4
 +//! Whether a file name option accepts multiple file names.
 +#define ffMULT  1<<5
 +//! Whether an input file name option accepts non-existent files.
 +#define ffALLOW_MISSING 1<<6
 +//! Convenience flag for an input/output file.
 +#define ffRW    (ffREAD | ffWRITE)
 +//! Convenience flag for an optional input file.
 +#define ffOPTRD (ffREAD | ffOPT)
 +//! Convenience flag for an optional output file.
 +#define ffOPTWR (ffWRITE| ffOPT)
 +//! Convenience flag for an optional input/output file.
 +#define ffOPTRW (ffRW   | ffOPT)
 +//! Convenience flag for a library input file.
 +#define ffLIBRD (ffREAD | ffLIB)
 +//! Convenience flag for an optional library input file.
 +#define ffLIBOPTRD (ffOPTRD | ffLIB)
 +//! Convenience flag for an input file that accepts multiple files.
 +#define ffRDMULT   (ffREAD  | ffMULT)
 +//! Convenience flag for an optional input file that accepts multiple files.
 +#define ffOPTRDMULT   (ffRDMULT | ffOPT)
 +//! Convenience flag for an output file that accepts multiple files.
 +#define ffWRMULT   (ffWRITE  | ffMULT)
 +//! Convenience flag for an optional output file that accepts multiple files.
 +#define ffOPTWRMULT   (ffWRMULT | ffOPT)
 +
 +/*! \brief
 + * Returns the filename belonging to cmd-line option opt, or NULL when
 + * no such option.
 + */
 +const char *opt2fn(const char *opt, int nfile, const t_filenm fnm[]);
 +
 +/*! \brief
 + * Returns the filenames belonging to cmd-line option opt, or NULL when
 + * no such option.
 + */
 +int opt2fns(char **fns[], const char *opt, int nfile,
 +            const t_filenm fnm[]);
 +
++/*! \brief
++ * Return a pointer to the t_filenm data structure of filenames belonging to
++ * command-line option opt, or NULL when no such option was used.
++ */
++const t_filenm *getFilenm(const char *opt, int nfile, const t_filenm fnm[]);
++
 +//! Returns a file pointer from the filename.
 +#define opt2FILE(opt, nfile, fnm, mode) gmx_ffopen(opt2fn(opt, nfile, fnm), mode)
 +
 +//! Returns the first file name with type ftp, or NULL when none found.
 +const char *ftp2fn(int ftp, int nfile, const t_filenm fnm[]);
 +
 +/*! \brief
 + * Returns the number of files for the first option with type ftp
 + * and the files in **fns[] (will be allocated), or NULL when none found.
 + */
 +int ftp2fns(char **fns[], int ftp, int nfile, const t_filenm fnm[]);
 +
 +//! Returns a file pointer from the file type.
 +#define ftp2FILE(ftp, nfile, fnm, mode) gmx_ffopen(ftp2fn(ftp, nfile, fnm), mode)
 +
 +//! Returns TRUE when this file type has been found on the cmd-line.
 +gmx_bool ftp2bSet(int ftp, int nfile, const t_filenm fnm[]);
 +
 +//! Returns TRUE when this option has been found on the cmd-line.
 +gmx_bool opt2bSet(const char *opt, int nfile, const t_filenm fnm[]);
 +
 +/*! \brief
 + * Returns the file name belonging top cmd-line option opt, or NULL when
 + * no such option.
 + *
 + * Also return NULL when opt is optional and option is not set.
 + */
 +const char *opt2fn_null(const char *opt, int nfile, const t_filenm fnm[]);
 +
 +/*! \brief
 + * Returns the first file name with type ftp, or NULL when none found.
 + *
 + * Also return NULL when ftp is optional and option is not set.
 + */
 +const char *ftp2fn_null(int ftp, int nfile, const t_filenm fnm[]);
 +
 +//! Returns whether or not this filenm is optional.
 +gmx_bool is_optional(const t_filenm *fnm);
 +
 +//! Returns whether or not this filenm is output.
 +gmx_bool is_output(const t_filenm *fnm);
 +
 +//! Returns whether or not this filenm is set.
 +gmx_bool is_set(const t_filenm *fnm);
 +
 +/*! \brief
 + * When we do checkpointing, this routine is called to check for previous
 + * output files and append a '.partNNNN' suffix before the (output) file extensions.
 + */
 +int add_suffix_to_output_names(t_filenm *fnm, int nfile, const char *suffix);
 +
 +/*! \brief
 + * Duplicates the filename list (to make a private copy for each thread,
 + * for example).
 + */
 +t_filenm *dup_tfn(int nf, const t_filenm tfn[]);
 +
 +//! Frees memory allocated for file names by parse_common_args().
 +void done_filenms(int nf, t_filenm fnm[]);
 +
 +//! \}
 +
 +#endif
index 9886c35b56ee4af19a93017d7a07181c79162f09,0000000000000000000000000000000000000000..2002498f344eb3a0493f2ec24207499497c09ed6
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,323 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "filetypes.h"
 +
 +#include <cstring>
 +
 +#include "gromacs/utility/arraysize.h"
 +#include "gromacs/utility/cstringutil.h"
 +
 +enum
 +{
 +    eftASC, eftXDR, eftTNG, eftGEN, eftNR
 +};
 +
 +/* To support multiple file types with one general (eg TRX) we have
 + * these arrays.
 + */
 +static const int trxs[] =
 +{
 +    efXTC, efTRR, efCPT,
 +    efGRO, efG96, efPDB, efTNG
 +};
 +#define NTRXS asize(trxs)
 +
 +static const int trcompressed[] =
 +{
 +    efXTC,
 +    efTNG
 +};
 +#define NTRCOMPRESSED asize(trcompressed)
 +
 +static const int tros[] =
 +{
 +    efXTC, efTRR,
 +    efGRO, efG96, efPDB, efTNG
 +};
 +#define NTROS asize(tros)
 +
 +static const int trns[] =
 +{
 +    efTRR, efCPT,
 +    efTNG
 +};
 +#define NTRNS asize(trns)
 +
 +static const int stos[] =
 +{ efGRO, efG96, efPDB, efBRK, efENT, efESP };
 +#define NSTOS asize(stos)
 +
 +static const int stxs[] =
 +{
 +    efGRO, efG96, efPDB, efBRK, efENT, efESP,
 +    efTPR
 +};
 +#define NSTXS asize(stxs)
 +
 +static const int tpss[] =
 +{
 +    efTPR,
 +    efGRO, efG96, efPDB, efBRK, efENT
 +};
 +#define NTPSS asize(tpss)
 +
 +typedef struct
 +{
 +    int         ftype;
 +    const char *ext;
 +    const char *defnm;
 +    const char *defopt;
 +    const char *descr;
 +    int         ntps;
 +    const int  *tps;
 +} t_deffile;
 +
 +/* this array should correspond to the enum in filetypes.h */
 +static const t_deffile deffile[efNR] =
 +{
 +    { eftASC, ".mdp", "grompp", "-f", "grompp input file with MD parameters" },
 +    { eftGEN, ".???", "traj", "-f", "Trajectory", NTRXS, trxs },
 +    { eftGEN, ".???", "trajout", "-f", "Trajectory", NTROS, tros },
 +    { eftGEN, ".???", "traj", NULL,
 +      "Full precision trajectory", NTRNS, trns },
 +    { eftXDR, ".trr", "traj", NULL, "Trajectory in portable xdr format" },
 +    { eftGEN, ".???", "traj_comp", NULL,
 +      "Compressed trajectory (tng format or portable xdr format)", NTRCOMPRESSED, trcompressed},
 +    { eftXDR, ".xtc", "traj", NULL,
 +      "Compressed trajectory (portable xdr format): xtc" },
 +    { eftTNG, ".tng", "traj", NULL,
 +      "Trajectory file (tng format)" },
 +    { eftXDR, ".edr", "ener",   NULL, "Energy file"},
 +    { eftGEN, ".???", "conf", "-c", "Structure file", NSTXS, stxs },
 +    { eftGEN, ".???", "out", "-o", "Structure file", NSTOS, stos },
 +    { eftASC, ".gro", "conf", "-c", "Coordinate file in Gromos-87 format" },
 +    { eftASC, ".g96", "conf", "-c", "Coordinate file in Gromos-96 format" },
 +    { eftASC, ".pdb", "eiwit",  "-f", "Protein data bank file"},
 +    { eftASC, ".brk", "eiwit",  "-f", "Brookhaven data bank file"},
 +    { eftASC, ".ent", "eiwit", "-f", "Entry in the protein date bank" },
 +    { eftASC, ".esp", "conf", "-f", "Coordinate file in Espresso format" },
 +    { eftASC, ".pqr", "state",  "-o", "Coordinate file for MEAD"},
 +    { eftXDR, ".cpt", "state",  "-cp", "Checkpoint file"},
 +    { eftASC, ".log", "run",    "-l", "Log file"},
 +    { eftASC, ".xvg", "graph",  "-o", "xvgr/xmgr file"},
 +    { eftASC, ".out", "hello",  "-o", "Generic output file"},
 +    { eftASC, ".ndx", "index",  "-n", "Index file", },
 +    { eftASC, ".top", "topol",  "-p", "Topology file"},
 +    { eftASC, ".itp", "topinc", NULL, "Include file for topology"},
 +    { eftGEN, ".???", "topol", "-s", "Structure+mass(db)", NTPSS, tpss },
 +    { eftXDR, ".tpr", "topol",  "-s", "Portable xdr run input file"},
 +    { eftASC, ".tex", "doc",    "-o", "LaTeX file"},
 +    { eftASC, ".rtp", "residue", NULL, "Residue Type file used by pdb2gmx" },
 +    { eftASC, ".atp", "atomtp", NULL, "Atomtype file used by pdb2gmx" },
 +    { eftASC, ".hdb", "polar",  NULL, "Hydrogen data base"},
 +    { eftASC, ".dat", "nnnice", NULL, "Generic data file"},
 +    { eftASC, ".dlg", "user",   NULL, "Dialog Box data for ngmx"},
 +    { eftASC, ".map", "ss", NULL, "File that maps matrix data to colors" },
 +    { eftASC, ".eps", "plot", NULL, "Encapsulated PostScript (tm) file" },
 +    { eftASC, ".mat", "ss",     NULL, "Matrix Data file"},
 +    { eftASC, ".m2p", "ps",     NULL, "Input file for mat2ps"},
 +    { eftXDR, ".mtx", "hessian", "-m", "Hessian matrix"},
 +    { eftASC, ".edi", "sam",    NULL, "ED sampling input"},
 +    { eftASC, ".cub", "pot",  NULL, "Gaussian cube file" },
 +    { eftASC, ".xpm", "root", NULL, "X PixMap compatible matrix file" },
 +    { eftASC, "", "rundir", NULL, "Run directory" }
 +};
 +
 +const char *ftp2ext(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].ext[0] != '\0' ? deffile[ftp].ext + 1 : "";
 +    }
 +    else
 +    {
 +        return "unknown";
 +    }
 +}
 +
 +const char *ftp2ext_generic(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        switch (ftp)
 +        {
 +            case efTRX:
 +                return "trx";
 +            case efTRN:
 +                return "trn";
 +            case efSTO:
 +                return "sto";
 +            case efSTX:
 +                return "stx";
 +            case efTPS:
 +                return "tps";
 +            default:
 +                return ftp2ext(ftp);
 +        }
 +    }
 +    else
 +    {
 +        return "unknown";
 +    }
 +}
 +
 +const char *ftp2ext_with_dot(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].ext;
 +    }
 +    else
 +    {
 +        return "unknown";
 +    }
 +}
 +
 +int ftp2generic_count(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].ntps;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +const int *ftp2generic_list(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].tps;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +const char *ftp2desc(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].descr;
 +    }
 +    else
 +    {
 +        return "unknown filetype";
 +    }
 +}
 +
 +gmx_bool ftp_is_text(int ftp)
 +{
 +    if ((ftp >= 0) && (ftp < efNR))
 +    {
 +        return deffile[ftp].ftype == eftASC;
 +    }
 +    return FALSE;
 +}
 +
 +gmx_bool ftp_is_xdr(int ftp)
 +{
 +    if ((ftp >= 0) && (ftp < efNR))
 +    {
 +        return deffile[ftp].ftype == eftXDR;
 +    }
 +    return FALSE;
 +}
 +
 +const char *ftp2defnm(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].defnm;
 +    }
 +    else
 +    {
 +        return NULL;
 +    }
 +}
 +
 +const char *ftp2defopt(int ftp)
 +{
 +    if ((0 <= ftp) && (ftp < efNR))
 +    {
 +        return deffile[ftp].defopt;
 +    }
 +    else
 +    {
 +        return NULL;
 +    }
 +}
 +
 +int fn2ftp(const char *fn)
 +{
 +    int         i, len;
 +    const char *feptr;
 +    const char *eptr;
 +
 +    if (!fn)
 +    {
 +        return efNR;
 +    }
 +
 +    len = std::strlen(fn);
 +    if ((len >= 4) && (fn[len - 4] == '.'))
 +    {
 +        feptr = &(fn[len - 4]);
 +    }
 +    else
 +    {
 +        return efNR;
 +    }
 +
 +    for (i = 0; (i < efNR); i++)
 +    {
 +        if ((eptr = deffile[i].ext) != NULL)
 +        {
 +            if (gmx_strcasecmp(feptr, eptr) == 0)
 +            {
 +                break;
 +            }
 +        }
 +    }
 +
 +    return i;
 +}
index 6ed1608b207b1b98ee179d2b9574a017387924c0,d5710ab84be444861abd26013fcb063f16a1c280..703a1cd471ad50196031a097fa5ad0d9349ed62e
   */
  #include "gmxpre.h"
  
 +#include "forcerec.h"
 +
  #include "config.h"
  
  #include <assert.h>
 -#include <math.h>
  #include <stdlib.h>
  #include <string.h>
  
 +#include <cmath>
 +
  #include <algorithm>
  
++#include "gromacs/commandline/filenm.h"
  #include "gromacs/domdec/domdec.h"
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/ewald/ewald.h"
 -#include "gromacs/fileio/filenm.h"
 -#include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
 -#include "gromacs/legacyheaders/copyrite.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/gmx_detect_hardware.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/inputrec.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/md_logging.h"
 -#include "gromacs/legacyheaders/md_support.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/nonbonded.h"
 -#include "gromacs/legacyheaders/ns.h"
 -#include "gromacs/legacyheaders/qmmm.h"
 -#include "gromacs/legacyheaders/tables.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/fileio/filetypes.h"
 +#include "gromacs/gmxlib/md_logging.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gmxlib/nonbonded/nonbonded.h"
 +#include "gromacs/gpu_utils/gpu_utils.h"
 +#include "gromacs/hardware/detecthardware.h"
  #include "gromacs/listed-forces/manage-threading.h"
 +#include "gromacs/listed-forces/pairs.h"
  #include "gromacs/math/calculate-ewald-splitting-coefficient.h"
 +#include "gromacs/math/functions.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/utilities.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/force.h"
  #include "gromacs/mdlib/forcerec-threading.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/md_support.h"
  #include "gromacs/mdlib/nb_verlet.h"
  #include "gromacs/mdlib/nbnxn_atomdata.h"
  #include "gromacs/mdlib/nbnxn_gpu_data_mgmt.h"
  #include "gromacs/mdlib/nbnxn_search.h"
  #include "gromacs/mdlib/nbnxn_simd.h"
 +#include "gromacs/mdlib/nbnxn_util.h"
 +#include "gromacs/mdlib/ns.h"
 +#include "gromacs/mdlib/qmmm.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/fcdata.h"
 +#include "gromacs/mdtypes/group.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/pbcutil/ishift.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/simd/simd.h"
 +#include "gromacs/tables/forcetable.h"
  #include "gromacs/topology/mtop_util.h"
 +#include "gromacs/trajectory/trajectoryframe.h"
 +#include "gromacs/utility/cstringutil.h"
+ #include "gromacs/utility/exceptions.h"
  #include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/gmxassert.h"
 +#include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
  #include "gromacs/utility/stringutil.h"
  
  #include "nbnxn_gpu_jit_support.h"
  
 +const char *egrp_nm[egNR+1] = {
 +    "Coul-SR", "LJ-SR", "Buck-SR",
 +    "Coul-14", "LJ-14", NULL
 +};
 +
  t_forcerec *mk_forcerec(void)
  {
      t_forcerec *fr;
@@@ -178,6 -164,7 +180,6 @@@ static real *make_ljpme_c6grid(const gm
      int        i, j, k, atnr;
      real       c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
      real      *grid;
 -    const real oneOverSix = 1.0 / 6.0;
  
      /* 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
              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);
 +            c6   = std::sqrt(c6i * c6j);
              if (fr->ljpme_combination_rule == eljpmeLB
                  && !gmx_numzero(c6) && !gmx_numzero(c12i) && !gmx_numzero(c12j))
              {
 -                sigmai = pow(c12i / c6i, oneOverSix);
 -                sigmaj = pow(c12j / c6j, oneOverSix);
 +                sigmai = gmx::sixthroot(c12i / c6i);
 +                sigmaj = gmx::sixthroot(c12j / c6j);
                  epsi   = c6i * c6i / c12i;
                  epsj   = c6j * c6j / c12j;
 -                c6     = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
 +                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5*(sigmai+sigmaj));
              }
              /* Store the elements at the same relative positions as C6 in nbfp in order
               * to simplify access in the kernels
@@@ -219,6 -206,7 +221,6 @@@ static real *mk_nbfp_combination_rule(c
      int        i, j, atnr;
      real       c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
      real       c6, c12;
 -    const real oneOverSix = 1.0 / 6.0;
  
      atnr = idef->atnr;
      snew(nbfp, 2*atnr*atnr);
              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);
 +            c6   = std::sqrt(c6i  * c6j);
 +            c12  = std::sqrt(c12i * c12j);
              if (comb_rule == eCOMB_ARITHMETIC
                  && !gmx_numzero(c6) && !gmx_numzero(c12))
              {
 -                sigmai = pow(c12i / c6i, oneOverSix);
 -                sigmaj = pow(c12j / c6j, oneOverSix);
 +                sigmai = gmx::sixthroot(c12i / c6i);
 +                sigmaj = gmx::sixthroot(c12j / c6j);
                  epsi   = c6i * c6i / c12i;
                  epsj   = c6j * c6j / c12j;
 -                c6     = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
 -                c12    = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 12);
 +                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5*(sigmai+sigmaj));
 +                c12    = std::sqrt(epsi * epsj) * gmx::power12(0.5*(sigmai+sigmaj));
              }
              C6(nbfp, atnr, i, j)   = c6*6.0;
              C12(nbfp, atnr, i, j)  = c12*12.0;
@@@ -1286,8 -1274,9 +1288,8 @@@ static void set_bham_b_max(FILE *fplog
      }
  }
  
 -static void make_nbf_tables(FILE *fp, const output_env_t oenv,
 +static void make_nbf_tables(FILE *fp,
                              t_forcerec *fr, real rtab,
 -                            const t_commrec *cr,
                              const char *tabfn, char *eg1, char *eg2,
                              t_nblists *nbl)
  {
          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);
 +    nbl->table_elec_vdw = make_tables(fp, fr, buf, rtab, 0);
      /* Copy the contents of the table to separate coulomb and LJ tables too,
       * to improve cache performance.
       */
       * 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++)
 +    snew(nbl->table_elec, 1);
 +    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->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);
 +
 +    snew(nbl->table_vdw, 1);
 +    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->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];
 +            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];
 +            nbl->table_vdw->data[8*i+j] = nbl->table_elec_vdw->data[12*i+4+j];
          }
      }
  }
  
+ /*!\brief If there's bonded interactions of type \c ftype1 or \c
+  * ftype2 present in the topology, build an array of the number of
+  * interactions present for each bonded interaction index found in the
+  * topology.
+  *
+  * \c ftype1 or \c ftype2 may be set to -1 to disable seeking for a
+  * valid type with that parameter.
+  *
+  * \c count will be reallocated as necessary to fit the largest bonded
+  * interaction index found, and its current size will be returned in
+  * \c ncount. It will contain zero for every bonded interaction index
+  * for which no interactions are present in the topology.
+  */
  static void count_tables(int ftype1, int ftype2, const gmx_mtop_t *mtop,
                           int *ncount, int **count)
  {
      const t_ilist       *il;
      int                  mt, ftype, stride, i, j, tabnr;
  
+     // Loop over all moleculetypes
      for (mt = 0; mt < mtop->nmoltype; mt++)
      {
          molt = &mtop->moltype[mt];
+         // Loop over all interaction types
          for (ftype = 0; ftype < F_NRE; ftype++)
          {
+             // If the current interaction type is one of the types whose tables we're trying to count...
              if (ftype == ftype1 || ftype == ftype2)
              {
                  il     = &molt->ilist[ftype];
                  stride = 1 + NRAL(ftype);
+                 // ... and there are actually some interactions for this type
                  for (i = 0; i < il->nr; i += stride)
                  {
+                     // Find out which table index the user wanted
                      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);
                      }
+                     // Make room for this index in the data structure
                      if (tabnr >= *ncount)
                      {
                          srenew(*count, tabnr+1);
                          }
                          *ncount = tabnr+1;
                      }
+                     // Record that this table index is used and must have a valid file
                      (*count)[tabnr]++;
                  }
              }
      }
  }
  
+ /*!\brief If there's bonded interactions of flavour \c tabext and type
+  * \c ftype1 or \c ftype2 present in the topology, seek them in the
+  * list of filenames passed to mdrun, and make bonded tables from
+  * those files.
+  *
+  * \c ftype1 or \c ftype2 may be set to -1 to disable seeking for a
+  * valid type with that parameter.
+  *
+  * A fatal error occurs if no matching filename is found.
+  */
  static bondedtable_t *make_bonded_tables(FILE *fplog,
                                           int ftype1, int ftype2,
                                           const gmx_mtop_t *mtop,
-                                          const char *basefn, const char *tabext)
+                                          const t_filenm *tabbfnm,
+                                          const char *tabext)
  {
-     int            i, ncount, *count;
-     char           tabfn[STRLEN];
+     int            ncount, *count;
      bondedtable_t *tab;
  
      tab = NULL;
      count  = NULL;
      count_tables(ftype1, ftype2, mtop, &ncount, &count);
  
+     // Are there any relevant tabulated bond interactions?
      if (ncount > 0)
      {
          snew(tab, ncount);
-         for (i = 0; i < ncount; i++)
+         for (int i = 0; i < ncount; i++)
          {
+             // Do any interactions exist that requires this table?
              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);
+                 // This pattern enforces the current requirement that
+                 // table filenames end in a characteristic sequence
+                 // before the file type extension, and avoids table 13
+                 // being recognized and used for table 1.
+                 std::string patternToFind = gmx::formatString("_%s%d.%s", tabext, i, ftp2ext(efXVG));
+                 bool        madeTable     = false;
+                 for (int j = 0; j < tabbfnm->nfiles && !madeTable; ++j)
+                 {
+                     std::string filename(tabbfnm->fns[j]);
+                     if (gmx::endsWith(filename, patternToFind))
+                     {
+                         // Finally read the table from the file found
+                         tab[i]    = make_bonded_table(fplog, tabbfnm->fns[j], NRAL(ftype1)-2);
+                         madeTable = true;
+                     }
+                 }
+                 if (!madeTable)
+                 {
+                     bool isPlural = (ftype2 != -1);
+                     gmx_fatal(FARGS, "Tabulated interaction of type '%s%s%s' with index %d cannot be used because no table file whose name matched '%s' was passed via the gmx mdrun -tableb command-line option.",
+                               interaction_function[ftype1].longname,
+                               isPlural ? "' or '" : "",
+                               isPlural ? interaction_function[ftype2].longname : "",
+                               i,
+                               patternToFind.c_str());
+                 }
              }
          }
          sfree(count);
@@@ -1444,6 -1487,11 +1500,6 @@@ void forcerec_set_ranges(t_forcerec *fr
      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)
@@@ -1471,6 -1519,37 +1527,6 @@@ static real cutoff_inf(real cutoff
      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;
@@@ -1564,7 -1643,7 +1620,7 @@@ static void pick_nbnxn_kernel_cpu(cons
      *kernel_type = nbnxnk4x4_PlainC;
      *ewald_excl  = ewaldexclTable;
  
 -#ifdef GMX_NBNXN_SIMD
 +#if GMX_SIMD
      {
  #ifdef GMX_NBNXN_SIMD_4XN
          *kernel_type = nbnxnk4xN_SIMD_4xN;
           */
          *kernel_type = nbnxnk4xN_SIMD_4xN;
  
 -#ifndef GMX_SIMD_HAVE_FMA
 +#if !GMX_SIMD_HAVE_FMA
          if (EEL_PME_EWALD(ir->coulombtype) ||
              EVDW_PME(ir->vdwtype))
          {
           * 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
 +        (GMX_SIMD_REAL_WIDTH >= 4 && GMX_SIMD_HAVE_FMA && !GMX_DOUBLE) || GMX_SIMD_IBM_QPX
          *ewald_excl = ewaldexclAnalytical;
  #endif
          if (getenv("GMX_NBNXN_EWALD_TABLE") != NULL)
          }
  
      }
 -#endif /* GMX_NBNXN_SIMD */
 +#endif // GMX_SIMD
  }
  
  
@@@ -1661,11 -1741,23 +1717,11 @@@ const char *lookup_nbnxn_kernel_name(in
              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
 +#if GMX_SIMD
              returnvalue = "SIMD";
 -#endif
 -#else  /* GMX_NBNXN_SIMD */
 +#else  // GMX_SIMD
              returnvalue = "not available";
 -#endif /* GMX_NBNXN_SIMD */
 +#endif // GMX_SIMD
              break;
          case nbnxnk8x8x8_GPU: returnvalue    = "GPU"; break;
          case nbnxnk8x8x8_PlainC: returnvalue = "plain C"; break;
@@@ -1725,8 -1817,8 +1781,8 @@@ static void pick_nbnxn_kernel(FIL
      {
          fprintf(fp, "\nUsing %s %dx%d non-bonded kernels\n\n",
                  lookup_nbnxn_kernel_name(*kernel_type),
 -                nbnxn_kernel_to_ci_size(*kernel_type),
 -                nbnxn_kernel_to_cj_size(*kernel_type));
 +                nbnxn_kernel_to_cluster_i_size(*kernel_type),
 +                nbnxn_kernel_to_cluster_j_size(*kernel_type));
  
          if (nbnxnk4x4_PlainC == *kernel_type ||
              nbnxnk8x8x8_PlainC == *kernel_type)
@@@ -1845,7 -1937,7 +1901,7 @@@ static void init_ewald_f_table(interact
      sfree_aligned(ic->tabq_vdw_F);
      sfree_aligned(ic->tabq_vdw_V);
  
 -    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype))
 +    if (EEL_PME_EWALD(ic->eeltype))
      {
          /* Create the original table data in FDV0 */
          snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32);
@@@ -1869,7 -1961,7 +1925,7 @@@ void init_interaction_const_tables(FIL
                                     interaction_const_t *ic,
                                     real                 rtab)
  {
 -    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype) || EVDW_PME(ic->vdwtype))
 +    if (EEL_PME_EWALD(ic->eeltype) || EVDW_PME(ic->vdwtype))
      {
          init_ewald_f_table(ic, rtab);
  
@@@ -1900,9 -1992,9 +1956,9 @@@ static void force_switch_constants(rea
       * 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);
 +    sc->c2   =  ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*gmx::square(rc - rsw));
 +    sc->c3   = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*gmx::power3(rc - rsw));
 +    sc->cpot = -pow(rc, -p) + p*sc->c2/3*gmx::power3(rc - rsw) + p*sc->c3/4*gmx::power4(rc - rsw);
  }
  
  static void potential_switch_constants(real rsw, real rc,
       * 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);
 +    sc->c3 = -10/gmx::power3(rc - rsw);
 +    sc->c4 =  15/gmx::power4(rc - rsw);
 +    sc->c5 =  -6/gmx::power5(rc - rsw);
  }
  
  /*! \brief Construct interaction constants
@@@ -1933,6 -2025,8 +1989,6 @@@ init_interaction_const(FIL
                         const t_forcerec           *fr)
  {
      interaction_const_t *ic;
 -    const real           minusSix          = -6.0;
 -    const real           minusTwelve       = -12.0;
  
      snew(ic, 1);
  
      snew_aligned(ic->tabq_coul_V, 16, 32);
  
      ic->rlist           = fr->rlist;
 -    ic->rlistlong       = fr->rlistlong;
  
      /* Lennard-Jones */
      ic->vdwtype         = fr->vdwtype;
      {
          case eintmodPOTSHIFT:
              /* Only shift the potential, don't touch the force */
 -            ic->dispersion_shift.cpot = -pow(ic->rvdw, minusSix);
 -            ic->repulsion_shift.cpot  = -pow(ic->rvdw, minusTwelve);
 +            ic->dispersion_shift.cpot = -1.0/gmx::power6(ic->rvdw);
 +            ic->repulsion_shift.cpot  = -1.0/gmx::power12(ic->rvdw);
              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, minusSix);
 +                crc2            = gmx::square(ic->ewaldcoeff_lj*ic->rvdw);
 +                ic->sh_lj_ewald = (std::exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)/gmx::power6(ic->rvdw);
              }
              break;
          case eintmodFORCESWITCH:
  
      if (fr->coulomb_modifier == eintmodPOTSHIFT)
      {
 -        ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb);
 +        ic->sh_ewald = std::erfc(ic->ewaldcoeff_q*ic->rcoulomb);
      }
      else
      {
@@@ -2146,10 -2241,7 +2202,10 @@@ static void init_nb_verlet(FIL
  
              bSimpleList = nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type);
  
 -            if (bSimpleList && (fr->vdwtype == evdwCUT && (fr->vdw_modifier == eintmodNONE || fr->vdw_modifier == eintmodPOTSHIFT)))
 +            if (fr->vdwtype == evdwCUT &&
 +                (fr->vdw_modifier == eintmodNONE ||
 +                 fr->vdw_modifier == eintmodPOTSHIFT) &&
 +                getenv("GMX_NO_LJ_COMB_RULE") == NULL)
              {
                  /* Plain LJ cut-off: we can optimize with combination rules */
                  enbnxninitcombrule = enbnxninitcombruleDETECT;
      {
          /* init the NxN GPU data; the last argument tells whether we'll have
           * both local and non-local NB calculation on GPU */
 -        nbnxn_gpu_init(fp, &nbv->gpu_nbv,
 +        nbnxn_gpu_init(&nbv->gpu_nbv,
                         &fr->hwinfo->gpu_info,
                         fr->gpu_opt,
                         fr->ic,
           * texture objects are used), but as this is initialization code, there
           * is no point in complicating things.
           */
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
          if (PAR(cr))
          {
              gmx_barrier(cr);
              char *end;
  
              nbv->min_ci_balanced = strtol(env, &end, 10);
 -            if (!end || (*end != 0) || nbv->min_ci_balanced <= 0)
 +            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);
 +                gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, non-negative integer required", env);
              }
  
              if (debug)
@@@ -2258,6 -2350,7 +2314,6 @@@ gmx_bool usingGpu(nonbonded_verlet_t *n
  }
  
  void init_forcerec(FILE              *fp,
 -                   const output_env_t oenv,
                     t_forcerec        *fr,
                     t_fcdata          *fcd,
                     const t_inputrec  *ir,
                     const t_commrec   *cr,
                     matrix             box,
                     const char        *tabfn,
 -                   const char        *tabafn,
                     const char        *tabpfn,
-                    const char        *tabbfn,
+                    const t_filenm    *tabbfnm,
                     const char        *nbpu_opt,
                     gmx_bool           bNoSolvOpt,
                     real               print_force)
      double         dbl;
      const t_block *cgs;
      gmx_bool       bGenericKernelOnly;
 -    gmx_bool       bMakeTables, bMakeSeparate14Table, bSomeNormalNbListsAreInUse;
 +    gmx_bool       needGroupSchemeTables, bSomeNormalNbListsAreInUse;
      gmx_bool       bFEP_NonBonded;
      int           *nm_ind, egp_flags;
  
          fr->n_tpi = 0;
      }
  
 -    /* Copy AdResS parameters */
 -    if (ir->bAdress)
 +    if (ir->coulombtype == eelRF_NEC_UNSUPPORTED)
      {
 -        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];
 -        }
 +        gmx_fatal(FARGS, "%s electrostatics is no longer supported",
 +                  eel_names[ir->coulombtype]);
 +    }
  
 -        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);
 +    if (ir->bAdress)
 +    {
 +        gmx_fatal(FARGS, "AdResS simulations are no longer supported");
      }
 -    else
 +    if (ir->useTwinRange)
      {
 -        fr->adress_type           = eAdressOff;
 -        fr->adress_do_hybridpairs = FALSE;
 +        gmx_fatal(FARGS, "Twin-range simulations are no longer supported");
      }
 -
      /* Copy the user determined parameters */
      fr->userint1  = ir->userint1;
      fr->userint2  = ir->userint2;
      if (ir->fepvals->bScCoul)
      {
          fr->sc_alphacoul  = ir->fepvals->sc_alpha;
 -        fr->sc_sigma6_min = pow(ir->fepvals->sc_sigma_min, 6);
 +        fr->sc_sigma6_min = gmx::power6(ir->fepvals->sc_sigma_min);
      }
      else
      {
      }
      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);
 +    fr->sc_sigma6_def = gmx::power6(ir->fepvals->sc_sigma);
  
      env = getenv("GMX_SCSIGMA_MIN");
      if (env != NULL)
      {
          dbl = 0;
          sscanf(env, "%20lf", &dbl);
 -        fr->sc_sigma6_min = pow(dbl, 6);
 +        fr->sc_sigma6_min = gmx::power6(dbl);
          if (fp)
          {
              fprintf(fp, "Setting the minimum soft core sigma to %g nm\n", dbl);
      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;
  
          case eelRF:
          case eelGRF:
 -        case eelRF_NEC:
              fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
              break;
  
      fr->rcoulomb         = cutoff_inf(ir->rcoulomb);
      fr->rcoulomb_switch  = ir->rcoulomb_switch;
  
 -    fr->bTwinRange = fr->rlistlong > fr->rlist;
 -    fr->bEwald     = (EEL_PME(fr->eeltype) || fr->eeltype == eelEWALD);
 +    fr->bEwald     = EEL_PME_EWALD(fr->eeltype);
  
      fr->reppow     = mtop->ffparams.reppow;
  
  
          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 ]);
 +            fprintf(fp, "Table routines are used for coulomb: %s\n",
 +                    gmx::boolToString(fr->bcoultab));
 +            fprintf(fp, "Table routines are used for vdw:     %s\n",
 +                    gmx::boolToString(fr->bvdwtab));
          }
  
          if (fr->bvdwtab == TRUE)
      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)
 +                       inputrecElecField(ir)
                         );
  
      if (fr->cutoff_scheme == ecutsGROUP &&
      }
  
      fr->eDispCorr = ir->eDispCorr;
 +    fr->numAtomsForDispersionCorrection = mtop->natoms;
      if (ir->eDispCorr != edispcNO)
      {
          set_avcsixtwelve(fp, fr, mtop);
      /* Generate the GB table if needed */
      if (fr->bGB)
      {
 -#ifdef GMX_DOUBLE
 +#if GMX_DOUBLE
          fr->gbtabscale = 2000;
  #else
          fr->gbtabscale = 500;
  #endif
  
          fr->gbtabr = 100;
 -        fr->gbtab  = make_gb_table(oenv, fr);
 +        fr->gbtab  = make_gb_table(fr);
  
          init_gb(&fr->born, fr, ir, mtop, ir->gb_algorithm);
  
      /*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->coulomb_modifier != eintmodNONE ||
 -                             fr->vdw_modifier != eintmodNONE ||
 -                             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));
 +    /* Construct tables for the group scheme. A little unnecessary to
 +     * make both vdw and coul tables sometimes, but what the
 +     * heck. Note that both cutoff schemes construct Ewald tables in
 +     * init_interaction_const_tables. */
 +    needGroupSchemeTables = (ir->cutoff_scheme == ecutsGROUP &&
 +                             (fr->bcoultab || fr->bvdwtab));
  
      negp_pp   = ir->opts.ngener - ir->nwall;
      negptable = 0;
 -    if (!bMakeTables)
 +    if (!needGroupSchemeTables)
      {
          bSomeNormalNbListsAreInUse = TRUE;
          fr->nnblists               = 1;
      }
      else
      {
 -        bSomeNormalNbListsAreInUse = (ir->eDispCorr != edispcNO);
 +        bSomeNormalNbListsAreInUse = FALSE;
          for (egi = 0; egi < negp_pp; egi++)
          {
              for (egj = egi; egj < negp_pp; egj++)
          }
      }
  
 -    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;
 +    rtab = ir->rlist + ir->tabext;
  
 -    if (bMakeTables)
 +    if (needGroupSchemeTables)
      {
          /* 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;
 -            }
 +            make_nbf_tables(fp, fr, rtab, tabfn, NULL, NULL, &fr->nblists[0]);
              m = 1;
          }
          else
                              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,
 +                        make_nbf_tables(fp, fr, rtab, 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)
              }
          }
      }
 -    else if ((fr->eDispCorr != edispcNO) &&
 -             ((fr->vdw_modifier == eintmodPOTSWITCH) ||
 -              (fr->vdw_modifier == eintmodFORCESWITCH) ||
 -              (fr->vdw_modifier == eintmodPOTSHIFT)))
 -    {
 -        /* Tables might not be used for the potential modifier interactions per se, but
 -         * we still need them to evaluate switch/shift dispersion corrections in this case.
 -         */
 -        make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[0]);
 -    }
  
 -    if (bMakeSeparate14Table)
 +    /* Tables might not be used for the potential modifier
 +     * interactions per se, but we still need them to evaluate
 +     * switch/shift dispersion corrections in this case. */
 +    if (fr->eDispCorr != edispcNO)
      {
 -        /* 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);
 +        fr->dispersionCorrectionTable = makeDispersionCorrectionTable(fp, fr, rtab, tabfn);
      }
  
 -    /* Read AdResS Thermo Force table if needed */
 -    if (fr->adress_icor == eAdressICThermoForce)
 +    /* We want to use unmodified tables for 1-4 coulombic
 +     * interactions, so we must in general have an extra set of
 +     * tables. */
 +    if (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)
      {
 -        /* 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);
 -        }
 +        fr->pairsTable = make_tables(fp, fr, tabpfn, rtab,
 +                                     GMX_MAKETABLES_14ONLY);
      }
  
      /* Wall stuff */
      fr->nwall = ir->nwall;
      if (ir->nwall && ir->wall_type == ewtTABLE)
      {
 -        make_wall_tables(fp, oenv, ir, tabfn, &mtop->groups, fr);
 +        make_wall_tables(fp, ir, tabfn, &mtop->groups, fr);
      }
  
-     if (fcd && tabbfn)
+     if (fcd && tabbfnm)
      {
-         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");
+         // Need to catch std::bad_alloc
+         // TODO Don't need to catch this here, when merging with master branch
+         try
+         {
+             fcd->bondtab  = make_bonded_tables(fp,
+                                                F_TABBONDS, F_TABBONDSNC,
+                                                mtop, tabbfnm, "b");
+             fcd->angletab = make_bonded_tables(fp,
+                                                F_TABANGLES, -1,
+                                                mtop, tabbfnm, "a");
+             fcd->dihtab   = make_bonded_tables(fp,
+                                                F_TABDIHS, -1,
+                                                mtop, tabbfnm, "d");
+         }
+         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
      }
      else
      {
      fr->timesteps = 0;
  
      /* Initialize neighbor search */
 -    init_ns(fp, cr, &fr->ns, fr, mtop);
 +    snew(fr->ns, 1);
 +    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_bonded_threading(fp, fr, mtop->groups.grps[egcENER].nr);
 +    init_bonded_threading(fp, mtop->groups.grps[egcENER].nr,
 +                          &fr->bonded_threading);
  
 -    snew(fr->excl_load, fr->nthreads+1);
 +    fr->nthread_ewc = gmx_omp_nthreads_get(emntBonded);
 +    snew(fr->ewc_t, fr->nthread_ewc);
 +    snew(fr->excl_load, fr->nthread_ewc + 1);
  
      /* fr->ic is used both by verlet and group kernels (to some extent) now */
      init_interaction_const(fp, &fr->ic, fr);
  
      if (fr->cutoff_scheme == ecutsVERLET)
      {
 -        if (ir->rcoulomb != ir->rvdw)
 +        // We checked the cut-offs in grompp, but double-check here.
 +        // We have PME+LJcutoff kernels for rcoulomb>rvdw.
 +        if (EEL_PME_EWALD(ir->coulombtype) && ir->vdwtype == eelCUT)
 +        {
 +            GMX_RELEASE_ASSERT(ir->rcoulomb >= ir->rvdw, "With Verlet lists and PME we should have rcoulomb>=rvdw");
 +        }
 +        else
          {
 -            gmx_fatal(FARGS, "With Verlet lists rcoulomb and rvdw should be identical");
 +            GMX_RELEASE_ASSERT(ir->rcoulomb == ir->rvdw, "With Verlet lists and no PME rcoulomb and rvdw should be identical");
          }
  
          init_nb_verlet(fp, &fr->nbv, bFEP_NonBonded, ir, fr, cr, nbpu_opt);
  
  #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])
 +#define pr_bool(fp, b) fprintf((fp), "%s: %s\n",#b, gmx::boolToString(b))
  
  void pr_forcerec(FILE *fp, t_forcerec *fr)
  {
      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_int(fp, fr->nblists[i].table_elec_vdw->n);
      }
      pr_real(fp, fr->rcoulomb_switch);
      pr_real(fp, fr->rcoulomb);
@@@ -3200,9 -3367,9 +3262,9 @@@ void forcerec_set_excl_load(t_forcere
      fr->excl_load[0] = 0;
      n                = 0;
      i                = 0;
 -    for (t = 1; t <= fr->nthreads; t++)
 +    for (t = 1; t <= fr->nthread_ewc; t++)
      {
 -        ntarget = (ntot*t)/fr->nthreads;
 +        ntarget = (ntot*t)/fr->nthread_ewc;
          while (i < top->excls.nr && n < ntarget)
          {
              for (j = ind[i]; j < ind[i+1]; j++)
@@@ -3238,20 -3405,14 +3300,20 @@@ void free_gpu_resources(const t_forcere
      {
          /* free nbnxn data in GPU memory */
          nbnxn_gpu_free(fr->nbv->gpu_nbv);
 +        /* stop the GPU profiler (only CUDA) */
 +        stopGpuProfiler();
  
          /* With tMPI we need to wait for all ranks to finish deallocation before
 -         * destroying the context in free_gpu() as some ranks may be sharing
 +         * destroying the CUDA context in free_gpu() as some tMPI ranks may be sharing
           * GPU and context.
 +         *
 +         * This is not a concern in OpenCL where we use one context per rank which
 +         * is freed in nbnxn_gpu_free().
 +         *
           * Note: as only PP ranks need to free GPU resources, so it is safe to
           * not call the barrier on PME ranks.
           */
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
          if (PAR(cr))
          {
              gmx_barrier(cr);
index 9ff5b250c1418a086645fce6960ed04a6fde779a,0000000000000000000000000000000000000000..274d909bf82446a41c62461e03acac7e2639f165
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,138 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
-  * \param[in]  tabbfn      Table potential file for bonded interactions
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifndef GMX_MDLIB_FORCEREC_H
 +#define GMX_MDLIB_FORCEREC_H
 +
 +#include "gromacs/mdlib/force_flags.h"
 +#include "gromacs/mdlib/genborn.h"
 +#include "gromacs/mdlib/tgroup.h"
 +#include "gromacs/mdlib/vsite.h"
 +#include "gromacs/mdtypes/forcerec.h"
 +#include "gromacs/timing/wallcycle.h"
 +
 +struct t_commrec;
 +struct t_fcdata;
++struct t_filenm;
 +
 +/*! \brief Create a new forcerec structure */
 +t_forcerec *mk_forcerec(void);
 +
 +/*! \brief Print the contents of the forcerec to a file
 + *
 + * \param[in] fplog The log file to print to
 + * \param[in] fr    The forcerec structure
 + */
 +void pr_forcerec(FILE *fplog, t_forcerec *fr);
 +
 +/*! \brief Set the number of charge groups and atoms.
 + *
 + * The force calculation needs information on which atoms it
 + * should do work.
 + * \param[inout] fr                  The forcerec
 + * \param[in]    ncg_home            Number of charge groups on this processor
 + * \param[in]    ncg_force           Number of charge groups to compute force on
 + * \param[in]    natoms_force        Number of atoms to compute force on
 + * \param[in]    natoms_force_constr Number of atoms involved in constraints
 + * \param[in]    natoms_f_novirsum   Number of atoms for which
 + *                                   force is to be compute but no virial
 + */
 +void
 +forcerec_set_ranges(t_forcerec *fr,
 +                    int ncg_home, int ncg_force,
 +                    int natoms_force,
 +                    int natoms_force_constr, int natoms_f_novirsum);
 +
 +/*! \brief Initiate table constants
 + *
 + * Initializes the tables in the interaction constant data structure.
 + * \param[in] fp   File for debugging output
 + * \param[in] ic   Structure holding the table constant
 + * \param[in] rtab The additional distance to add to tables
 + */
 +void init_interaction_const_tables(FILE                   *fp,
 +                                   interaction_const_t    *ic,
 +                                   real                    rtab);
 +
 +/*! \brief Initialize forcerec structure.
 + *
 + * The Force rec struct must be created with mk_forcerec.
 + * \param[in]  fplog       File for printing
 + * \param[out] fr          The forcerec
 + * \param[in]  fcd         Force constant data
 + * \param[in]  ir          Inputrec structure
 + * \param[in]  mtop        Molecular topology
 + * \param[in]  cr          Communication structures
 + * \param[in]  box         Simulation box
 + * \param[in]  tabfn       Table potential file for non-bonded interactions
 + * \param[in]  tabpfn      Table potential file for pair interactions
-                    const char             *tabbfn,
++ * \param[in]  tabbfnm     Table potential files for bonded interactions
 + * \param[in]  nbpu_opt    Nonbonded Processing Unit (GPU/CPU etc.)
 + * \param[in]  bNoSolvOpt  Do not use solvent optimization
 + * \param[in]  print_force Print forces for atoms with force >= print_force
 + */
 +void init_forcerec(FILE                   *fplog,
 +                   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             *tabpfn,
++                   const t_filenm         *tabbfnm,
 +                   const char             *nbpu_opt,
 +                   gmx_bool                bNoSolvOpt,
 +                   real                    print_force);
 +
 +/*! \brief Divide exclusions over threads
 + *
 + * Set the exclusion load for the local exclusions and possibly threads
 + * \param[out] fr  The force record
 + * \param[in]  top The topology
 + */
 +void forcerec_set_excl_load(t_forcerec           *fr,
 +                            const gmx_localtop_t *top);
 +
 +/*! \brief Update parameters dependent on box
 + *
 + * Updates parameters in the forcerec that are time dependent
 + * \param[out] fr  The force record
 + * \param[in]  box The simulation box
 + */
 +void update_forcerec(t_forcerec *fr, matrix box);
 +
 +#endif
index ffa5825bec61fec865ce2060ebab9732e8ba7b0f,0000000000000000000000000000000000000000..63ff5e8b495b08bf6ed2715c681312d3aeaa1214
mode 100644,000000..100644
--- /dev/null
@@@ -1,435 -1,0 +1,438 @@@
-  * Copyright (c) 2012,2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
-                     /* Set the mass of completely frozen particles to ALMOST_ZERO iso 0
-                      * to avoid div by zero in lincs or shake.
-                      * Note that constraints can still move a partially frozen particle.
++ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "mdatoms.h"
 +
 +#include <math.h>
 +
 +#include "gromacs/math/functions.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/qmmm.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/topology/mtop_util.h"
 +#include "gromacs/topology/topology.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +#define ALMOST_ZERO 1e-30
 +
 +t_mdatoms *init_mdatoms(FILE *fp, const gmx_mtop_t *mtop, gmx_bool bFreeEnergy)
 +{
 +    int                     a;
 +    double                  tmA, tmB;
 +    t_atom                 *atom;
 +    t_mdatoms              *md;
 +    gmx_mtop_atomloop_all_t aloop;
 +
 +    snew(md, 1);
 +
 +    md->nenergrp = mtop->groups.grps[egcENER].nr;
 +    md->bVCMgrps = FALSE;
 +    tmA          = 0.0;
 +    tmB          = 0.0;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        if (ggrpnr(&mtop->groups, egcVCM, a) > 0)
 +        {
 +            md->bVCMgrps = TRUE;
 +        }
 +
 +        if (bFreeEnergy && PERTURBED(*atom))
 +        {
 +            md->nPerturbed++;
 +            if (atom->mB != atom->m)
 +            {
 +                md->nMassPerturbed++;
 +            }
 +            if (atom->qB != atom->q)
 +            {
 +                md->nChargePerturbed++;
 +            }
 +            if (atom->typeB != atom->type)
 +            {
 +                md->nTypePerturbed++;
 +            }
 +        }
 +
 +        tmA += atom->m;
 +        tmB += atom->mB;
 +    }
 +
 +    md->tmassA = tmA;
 +    md->tmassB = tmB;
 +
 +    if (bFreeEnergy && fp)
 +    {
 +        fprintf(fp,
 +                "There are %d atoms and %d charges for free energy perturbation\n",
 +                md->nPerturbed, md->nChargePerturbed);
 +    }
 +
 +    md->bOrires = gmx_mtop_ftype_count(mtop, F_ORIRES);
 +
 +    return md;
 +}
 +
 +void atoms2md(const gmx_mtop_t *mtop, const t_inputrec *ir,
 +              int nindex, const int *index,
 +              int homenr,
 +              t_mdatoms *md)
 +{
 +    gmx_bool              bLJPME;
 +    gmx_mtop_atomlookup_t alook;
 +    int                   i;
 +    const t_grpopts      *opts;
 +    const gmx_groups_t   *groups;
 +    int                   nthreads gmx_unused;
 +
 +    bLJPME = EVDW_PME(ir->vdwtype);
 +
 +    opts = &ir->opts;
 +
 +    groups = &mtop->groups;
 +
 +    /* Index==NULL indicates no DD (unless we have a DD node with no
 +     * atoms), so also check for homenr. This should be
 +     * signaled properly with an extra parameter or nindex==-1.
 +     */
 +    if (index == NULL && (homenr > 0))
 +    {
 +        md->nr = mtop->natoms;
 +    }
 +    else
 +    {
 +        md->nr = nindex;
 +    }
 +
 +    if (md->nr > md->nalloc)
 +    {
 +        md->nalloc = over_alloc_dd(md->nr);
 +
 +        if (md->nMassPerturbed)
 +        {
 +            srenew(md->massA, md->nalloc);
 +            srenew(md->massB, md->nalloc);
 +        }
 +        srenew(md->massT, md->nalloc);
 +        srenew(md->invmass, md->nalloc);
 +        srenew(md->chargeA, md->nalloc);
 +        srenew(md->typeA, md->nalloc);
 +        if (md->nPerturbed)
 +        {
 +            srenew(md->chargeB, md->nalloc);
 +            srenew(md->typeB, md->nalloc);
 +        }
 +        if (bLJPME)
 +        {
 +            srenew(md->sqrt_c6A, md->nalloc);
 +            srenew(md->sigmaA, md->nalloc);
 +            srenew(md->sigma3A, md->nalloc);
 +            if (md->nPerturbed)
 +            {
 +                srenew(md->sqrt_c6B, md->nalloc);
 +                srenew(md->sigmaB, md->nalloc);
 +                srenew(md->sigma3B, md->nalloc);
 +            }
 +        }
 +        srenew(md->ptype, md->nalloc);
 +        if (opts->ngtc > 1)
 +        {
 +            srenew(md->cTC, md->nalloc);
 +            /* We always copy cTC with domain decomposition */
 +        }
 +        srenew(md->cENER, md->nalloc);
 +        if (opts->ngacc > 1)
 +        {
 +            srenew(md->cACC, md->nalloc);
 +        }
 +        if (opts->nFreeze &&
 +            (opts->ngfrz > 1 ||
 +             opts->nFreeze[0][XX] || opts->nFreeze[0][YY] || opts->nFreeze[0][ZZ]))
 +        {
 +            srenew(md->cFREEZE, md->nalloc);
 +        }
 +        if (md->bVCMgrps)
 +        {
 +            srenew(md->cVCM, md->nalloc);
 +        }
 +        if (md->bOrires)
 +        {
 +            srenew(md->cORF, md->nalloc);
 +        }
 +        if (md->nPerturbed)
 +        {
 +            srenew(md->bPerturbed, md->nalloc);
 +        }
 +
 +        /* Note that these user t_mdatoms array pointers are NULL
 +         * when there is only one group present.
 +         * Therefore, when adding code, the user should use something like:
 +         * gprnrU1 = (md->cU1==NULL ? 0 : md->cU1[localatindex])
 +         */
 +        if (mtop->groups.grpnr[egcUser1] != NULL)
 +        {
 +            srenew(md->cU1, md->nalloc);
 +        }
 +        if (mtop->groups.grpnr[egcUser2] != NULL)
 +        {
 +            srenew(md->cU2, md->nalloc);
 +        }
 +
 +        if (ir->bQMMM)
 +        {
 +            srenew(md->bQM, md->nalloc);
 +        }
 +    }
 +
 +    alook = gmx_mtop_atomlookup_init(mtop);
 +
 +    // cppcheck-suppress unreadVariable
 +    nthreads = gmx_omp_nthreads_get(emntDefault);
 +#pragma omp parallel for num_threads(nthreads) schedule(static)
 +    for (i = 0; i < md->nr; i++)
 +    {
 +        try
 +        {
 +            int      g, ag;
 +            real     mA, mB, fac;
 +            real     c6, c12;
 +            t_atom  *atom;
 +
 +            if (index == NULL)
 +            {
 +                ag = i;
 +            }
 +            else
 +            {
 +                ag   = index[i];
 +            }
 +            gmx_mtop_atomnr_to_atom(alook, ag, &atom);
 +
 +            if (md->cFREEZE)
 +            {
 +                md->cFREEZE[i] = ggrpnr(groups, egcFREEZE, ag);
 +            }
 +            if (EI_ENERGY_MINIMIZATION(ir->eI))
 +            {
 +                /* Displacement is proportional to F, masses used for constraints */
 +                mA = 1.0;
 +                mB = 1.0;
 +            }
 +            else if (ir->eI == eiBD)
 +            {
 +                /* With BD the physical masses are irrelevant.
 +                 * To keep the code simple we use most of the normal MD code path
 +                 * for BD. Thus for constraining the masses should be proportional
 +                 * to the friction coefficient. We set the absolute value such that
 +                 * m/2<(dx/dt)^2> = m/2*2kT/fric*dt = kT/2 => m=fric*dt/2
 +                 * Then if we set the (meaningless) velocity to v=dx/dt, we get the
 +                 * correct kinetic energy and temperature using the usual code path.
 +                 * Thus with BD v*dt will give the displacement and the reported
 +                 * temperature can signal bad integration (too large time step).
 +                 */
 +                if (ir->bd_fric > 0)
 +                {
 +                    mA = 0.5*ir->bd_fric*ir->delta_t;
 +                    mB = 0.5*ir->bd_fric*ir->delta_t;
 +                }
 +                else
 +                {
 +                    /* The friction coefficient is mass/tau_t */
 +                    fac = ir->delta_t/opts->tau_t[md->cTC ? groups->grpnr[egcTC][ag] : 0];
 +                    mA  = 0.5*atom->m*fac;
 +                    mB  = 0.5*atom->mB*fac;
 +                }
 +            }
 +            else
 +            {
 +                mA = atom->m;
 +                mB = atom->mB;
 +            }
 +            if (md->nMassPerturbed)
 +            {
 +                md->massA[i]  = mA;
 +                md->massB[i]  = mB;
 +            }
 +            md->massT[i]    = mA;
 +            if (mA == 0.0)
 +            {
 +                md->invmass[i]    = 0;
 +            }
 +            else if (md->cFREEZE)
 +            {
 +                g = md->cFREEZE[i];
 +                if (opts->nFreeze[g][XX] && opts->nFreeze[g][YY] && opts->nFreeze[g][ZZ])
 +                {
++                    /* Set the mass of completely frozen particles to ALMOST_ZERO
++                     * iso 0 to avoid div by zero in lincs or shake.
 +                     */
 +                    md->invmass[i]  = ALMOST_ZERO;
 +                }
 +                else
 +                {
++                    /* Note: Partially frozen particles use the normal invmass.
++                     * If such particles are constrained, the frozen dimensions
++                     * should not be updated with the constrained coordinates.
++                     */
 +                    md->invmass[i]  = 1.0/mA;
 +                }
 +            }
 +            else
 +            {
 +                md->invmass[i]    = 1.0/mA;
 +            }
 +            md->chargeA[i]      = atom->q;
 +            md->typeA[i]        = atom->type;
 +            if (bLJPME)
 +            {
 +                c6                = mtop->ffparams.iparams[atom->type*(mtop->ffparams.atnr+1)].lj.c6;
 +                c12               = mtop->ffparams.iparams[atom->type*(mtop->ffparams.atnr+1)].lj.c12;
 +                md->sqrt_c6A[i]   = sqrt(c6);
 +                if (c6 == 0.0 || c12 == 0)
 +                {
 +                    md->sigmaA[i] = 1.0;
 +                }
 +                else
 +                {
 +                    md->sigmaA[i] = gmx::sixthroot(c12/c6);
 +                }
 +                md->sigma3A[i]    = 1/(md->sigmaA[i]*md->sigmaA[i]*md->sigmaA[i]);
 +            }
 +            if (md->nPerturbed)
 +            {
 +                md->bPerturbed[i] = PERTURBED(*atom);
 +                md->chargeB[i]    = atom->qB;
 +                md->typeB[i]      = atom->typeB;
 +                if (bLJPME)
 +                {
 +                    c6                = mtop->ffparams.iparams[atom->typeB*(mtop->ffparams.atnr+1)].lj.c6;
 +                    c12               = mtop->ffparams.iparams[atom->typeB*(mtop->ffparams.atnr+1)].lj.c12;
 +                    md->sqrt_c6B[i]   = sqrt(c6);
 +                    if (c6 == 0.0 || c12 == 0)
 +                    {
 +                        md->sigmaB[i] = 1.0;
 +                    }
 +                    else
 +                    {
 +                        md->sigmaB[i] = gmx::sixthroot(c12/c6);
 +                    }
 +                    md->sigma3B[i]    = 1/(md->sigmaB[i]*md->sigmaB[i]*md->sigmaB[i]);
 +                }
 +            }
 +            md->ptype[i]    = atom->ptype;
 +            if (md->cTC)
 +            {
 +                md->cTC[i]    = groups->grpnr[egcTC][ag];
 +            }
 +            md->cENER[i]    =
 +                (groups->grpnr[egcENER] ? groups->grpnr[egcENER][ag] : 0);
 +            if (md->cACC)
 +            {
 +                md->cACC[i]   = groups->grpnr[egcACC][ag];
 +            }
 +            if (md->cVCM)
 +            {
 +                md->cVCM[i]       = groups->grpnr[egcVCM][ag];
 +            }
 +            if (md->cORF)
 +            {
 +                md->cORF[i]       = groups->grpnr[egcORFIT][ag];
 +            }
 +
 +            if (md->cU1)
 +            {
 +                md->cU1[i]        = groups->grpnr[egcUser1][ag];
 +            }
 +            if (md->cU2)
 +            {
 +                md->cU2[i]        = groups->grpnr[egcUser2][ag];
 +            }
 +
 +            if (ir->bQMMM)
 +            {
 +                if (groups->grpnr[egcQMMM] == 0 ||
 +                    groups->grpnr[egcQMMM][ag] < groups->grps[egcQMMM].nr-1)
 +                {
 +                    md->bQM[i]      = TRUE;
 +                }
 +                else
 +                {
 +                    md->bQM[i]      = FALSE;
 +                }
 +            }
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +
 +    md->homenr = homenr;
 +    md->lambda = 0;
 +}
 +
 +void update_mdatoms(t_mdatoms *md, real lambda)
 +{
 +    int    al, end;
 +    real   L1 = 1.0-lambda;
 +
 +    end = md->nr;
 +
 +    if (md->nMassPerturbed)
 +    {
 +        for (al = 0; (al < end); al++)
 +        {
 +            if (md->bPerturbed[al])
 +            {
 +                md->massT[al] = L1*md->massA[al]+ lambda*md->massB[al];
 +                if (md->invmass[al] > 1.1*ALMOST_ZERO)
 +                {
 +                    md->invmass[al] = 1.0/md->massT[al];
 +                }
 +            }
 +        }
 +        md->tmass = L1*md->tmassA + lambda*md->tmassB;
 +    }
 +    else
 +    {
 +        md->tmass = md->tmassA;
 +    }
 +    md->lambda = lambda;
 +}
index 39464192315a153f2931594c615d404cce44c7ff,0429445dd034b982a95101ffcfa4e678ccd81b36..058680e11d6f18d096d93a4b4cefb05a0423656a
  #include <limits>
  #endif
  
 -#include <cuda.h>
  
 -#ifdef TMPI_ATOMICS
 -#include "thread_mpi/atomic.h"
 -#endif
 -
 -#include "gromacs/gmxlib/cuda_tools/cudautils.cuh"
 -#include "gromacs/legacyheaders/types/force_flags.h"
 -#include "gromacs/legacyheaders/types/simple.h"
 +#include "gromacs/gpu_utils/cudautils.cuh"
 +#include "gromacs/mdlib/force_flags.h"
  #include "gromacs/mdlib/nb_verlet.h"
 -#include "gromacs/mdlib/nbnxn_consts.h"
  #include "gromacs/mdlib/nbnxn_gpu_data_mgmt.h"
  #include "gromacs/mdlib/nbnxn_pairlist.h"
 -#include "gromacs/pbcutil/ishift.h"
  #include "gromacs/timing/gpu_timing.h"
  #include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/gmxassert.h"
  
  #include "nbnxn_cuda_types.h"
  
@@@ -71,17 -78,20 +71,18 @@@ texture<float, 1, cudaReadModeElementTy
  /*! Texture reference for Ewald coulomb force table; bound to cu_nbparam_t.coulomb_tab */
  texture<float, 1, cudaReadModeElementType> coulomb_tab_texref;
  
 -/* Convenience defines */
 -#define NCL_PER_SUPERCL         (NBNXN_GPU_NCLUSTER_PER_SUPERCLUSTER)
 -#define CL_SIZE                 (NBNXN_GPU_CLUSTER_SIZE)
  
 -/***** The kernels come here *****/
 +/***** The kernel declarations/definitions come here *****/
+ #include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_utils.cuh"
  
 -/* Top-level kernel generation: will generate through multiple inclusion the
 - * following flavors for all kernels:
 +/* Top-level kernel declaration generation: will generate through multiple
 + * inclusion the following flavors for all kernel declarations:
   * - force-only output;
   * - force and energy output;
   * - force-only with pair list pruning;
   * - force and energy output with pair list pruning.
   */
 +#define FUNCTION_DECLARATION_ONLY
  /** Force only **/
  #include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernels.cuh"
  /** Force & energy **/
  #include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernels.cuh"
  #undef CALC_ENERGIES
  #undef PRUNE_NBL
 +#undef FUNCTION_DECLARATION_ONLY
 +
 +/* Now generate the function definitions if we are using a single compilation unit. */
 +#if GMX_CUDA_NB_SINGLE_COMPILATION_UNIT
 +#include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_F_noprune.cu"
 +#include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_F_prune.cu"
 +#include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_VF_noprune.cu"
 +#include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_VF_prune.cu"
 +#else
 +/* Prevent compilation in multiple compilation unit mode for CC 2.x. Although we have
 + * build-time checks to prevent this, the user could manually tweaks nvcc flags
 + * which would lead to buggy kernels getting compiled.
 + */
 +#if GMX_PTX_ARCH > 0 && GMX_PTX_ARCH <= 210
 +#error Due to an CUDA compiler bug, the CUDA non-bonded module can not be compiled with multiple compilation units for CC 2.x devices. If you have changed the nvcc flags manually, either use the GMX_CUDA_TARGET_* variables instead or set GMX_CUDA_NB_SINGLE_COMPILATION_UNIT=ON CMake option.
 +#endif
 +#endif /* GMX_CUDA_NB_SINGLE_COMPILATION_UNIT */
 +
  
  
  /*! Nonbonded kernel function pointer type */
@@@ -126,17 -118,18 +127,17 @@@ typedef void (*nbnxn_cu_kfunc_ptr_t)(co
  
  /*********************************/
  
 +/* XXX switch between chevron and cudaLaunch (supported only in CUDA >=7.0)
 +   -- only for benchmarking purposes */
 +static const bool bUseCudaLaunchKernel =
 +    (GMX_CUDA_VERSION >= 7000) && (getenv("GMX_DISABLE_CUDALAUNCH") == NULL);
 +
  /* XXX always/never run the energy/pruning kernels -- only for benchmarking purposes */
  static bool always_ener  = (getenv("GMX_GPU_ALWAYS_ENER") != NULL);
  static bool never_ener   = (getenv("GMX_GPU_NEVER_ENER") != NULL);
  static bool always_prune = (getenv("GMX_GPU_ALWAYS_PRUNE") != NULL);
  
  
 -/* Bit-pattern used for polling-based GPU synchronization. It is used as a float
 - * and corresponds to having the exponent set to the maximum (127 -- single
 - * precision) and the mantissa to 0.
 - */
 -static unsigned int poll_wait_pattern = (0x7FU << 23);
 -
  /*! Returns the number of blocks to be used for the nonbonded GPU kernel. */
  static inline int calc_nb_kernel_nblock(int nwork_units, gmx_device_info_t *dinfo)
  {
  /*! Force-only kernel function pointers. */
  static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_noprune_ptr[eelCuNR][evdwCuNR] =
  {
 -    { nbnxn_kernel_ElecCut_VdwLJ_F_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_F_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_F_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_F_cuda            },
 -    { nbnxn_kernel_ElecRF_VdwLJ_F_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_F_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_F_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_F_cuda             },
 -    { nbnxn_kernel_ElecEwQSTab_VdwLJ_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_F_cuda        },
 -    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_F_cuda },
 -    { nbnxn_kernel_ElecEw_VdwLJ_F_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_F_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_F_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_F_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_F_cuda             },
 -    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_F_cuda      }
 +    { nbnxn_kernel_ElecCut_VdwLJ_F_cuda,            nbnxn_kernel_ElecCut_VdwLJCombGeom_F_cuda,            nbnxn_kernel_ElecCut_VdwLJCombLB_F_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_F_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_F_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_F_cuda            },
 +    { nbnxn_kernel_ElecRF_VdwLJ_F_cuda,             nbnxn_kernel_ElecRF_VdwLJCombGeom_F_cuda,             nbnxn_kernel_ElecRF_VdwLJCombLB_F_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_F_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_F_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_F_cuda             },
 +    { nbnxn_kernel_ElecEwQSTab_VdwLJ_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombGeom_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombLB_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_F_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_F_cuda        },
 +    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombGeom_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombLB_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_F_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_F_cuda },
 +    { nbnxn_kernel_ElecEw_VdwLJ_F_cuda,             nbnxn_kernel_ElecEw_VdwLJCombGeom_F_cuda,             nbnxn_kernel_ElecEw_VdwLJCombLB_F_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_F_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_F_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_F_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_F_cuda             },
 +    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_F_cuda      }
  };
  
  /*! Force + energy kernel function pointers. */
  static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_noprune_ptr[eelCuNR][evdwCuNR] =
  {
 -    { nbnxn_kernel_ElecCut_VdwLJ_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_VF_cuda              },
 -    { nbnxn_kernel_ElecRF_VdwLJ_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_VF_cuda               },
 -    { nbnxn_kernel_ElecEwQSTab_VdwLJ_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_VF_cuda          },
 -    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_VF_cuda     },
 -    { nbnxn_kernel_ElecEw_VdwLJ_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_VF_cuda               },
 -    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_VF_cuda        }
 +    { nbnxn_kernel_ElecCut_VdwLJ_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJCombGeom_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJCombLB_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_VF_cuda            },
 +    { nbnxn_kernel_ElecRF_VdwLJ_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJCombGeom_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJCombLB_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_VF_cuda             },
 +    { nbnxn_kernel_ElecEwQSTab_VdwLJ_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombGeom_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombLB_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_VF_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_VF_cuda        },
 +    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombGeom_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombLB_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_VF_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_VF_cuda },
 +    { nbnxn_kernel_ElecEw_VdwLJ_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJCombGeom_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJCombLB_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VF_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_VF_cuda             },
 +    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_VF_cuda      }
  };
  
  /*! Force + pruning kernel function pointers. */
  static const nbnxn_cu_kfunc_ptr_t nb_kfunc_noener_prune_ptr[eelCuNR][evdwCuNR] =
  {
 -    { nbnxn_kernel_ElecCut_VdwLJ_F_prune_cuda,             nbnxn_kernel_ElecCut_VdwLJFsw_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_F_prune_cuda            },
 -    { nbnxn_kernel_ElecRF_VdwLJ_F_prune_cuda,              nbnxn_kernel_ElecRF_VdwLJFsw_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_F_prune_cuda             },
 -    { nbnxn_kernel_ElecEwQSTab_VdwLJ_F_prune_cuda,         nbnxn_kernel_ElecEwQSTab_VdwLJFsw_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_F_prune_cuda        },
 -    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_F_prune_cuda,  nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_F_prune_cuda },
 -    { nbnxn_kernel_ElecEw_VdwLJ_F_prune_cuda,              nbnxn_kernel_ElecEw_VdwLJFsw_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_F_prune_cuda             },
 -    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_F_prune_cuda,       nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_F_prune_cuda      }
 +    { nbnxn_kernel_ElecCut_VdwLJ_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJCombGeom_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJCombLB_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_F_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_F_prune_cuda             },
 +    { nbnxn_kernel_ElecRF_VdwLJ_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJCombGeom_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJCombLB_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_F_prune_cuda              },
 +    { nbnxn_kernel_ElecEwQSTab_VdwLJ_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombGeom_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombLB_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_F_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_F_prune_cuda         },
 +    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombGeom_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombLB_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_F_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_F_prune_cuda  },
 +    { nbnxn_kernel_ElecEw_VdwLJ_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJCombGeom_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJCombLB_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_F_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_F_prune_cuda              },
 +    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_F_prune_cuda       }
  };
  
  /*! Force + energy + pruning kernel function pointers. */
  static const nbnxn_cu_kfunc_ptr_t nb_kfunc_ener_prune_ptr[eelCuNR][evdwCuNR] =
  {
 -    { nbnxn_kernel_ElecCut_VdwLJ_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_VF_prune_cuda            },
 -    { nbnxn_kernel_ElecRF_VdwLJ_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_VF_prune_cuda             },
 -    { nbnxn_kernel_ElecEwQSTab_VdwLJ_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_VF_prune_cuda        },
 -    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_VF_prune_cuda },
 -    { nbnxn_kernel_ElecEw_VdwLJ_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_VF_prune_cuda             },
 -    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_VF_prune_cuda      }
 +    { nbnxn_kernel_ElecCut_VdwLJ_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJCombGeom_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJCombLB_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJFsw_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJPsw_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombGeom_VF_prune_cuda,            nbnxn_kernel_ElecCut_VdwLJEwCombLB_VF_prune_cuda            },
 +    { nbnxn_kernel_ElecRF_VdwLJ_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJCombGeom_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJCombLB_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJFsw_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJPsw_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_prune_cuda,             nbnxn_kernel_ElecRF_VdwLJEwCombLB_VF_prune_cuda             },
 +    { nbnxn_kernel_ElecEwQSTab_VdwLJ_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombGeom_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJCombLB_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJFsw_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJPsw_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombGeom_VF_prune_cuda,        nbnxn_kernel_ElecEwQSTab_VdwLJEwCombLB_VF_prune_cuda        },
 +    { nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJ_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombGeom_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJCombLB_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJFsw_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJPsw_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombGeom_VF_prune_cuda, nbnxn_kernel_ElecEwQSTabTwinCut_VdwLJEwCombLB_VF_prune_cuda },
 +    { nbnxn_kernel_ElecEw_VdwLJ_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJCombGeom_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJCombLB_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJFsw_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJPsw_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombGeom_VF_prune_cuda,             nbnxn_kernel_ElecEw_VdwLJEwCombLB_VF_prune_cuda             },
 +    { nbnxn_kernel_ElecEwTwinCut_VdwLJ_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJCombLB_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJFsw_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJPsw_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_prune_cuda,      nbnxn_kernel_ElecEwTwinCut_VdwLJEwCombLB_VF_prune_cuda      }
  };
  
  /*! Return a pointer to the kernel version to be executed at the current step. */
 -static inline nbnxn_cu_kfunc_ptr_t select_nbnxn_kernel(int  eeltype,
 -                                                       int  evdwtype,
 -                                                       bool bDoEne,
 -                                                       bool bDoPrune)
 +static inline nbnxn_cu_kfunc_ptr_t select_nbnxn_kernel(int                                  eeltype,
 +                                                       int                                  evdwtype,
 +                                                       bool                                 bDoEne,
 +                                                       bool                                 bDoPrune,
 +                                                       struct gmx_device_info_t gmx_unused *devInfo)
  {
      nbnxn_cu_kfunc_ptr_t res;
  
 -    assert(eeltype < eelCuNR);
 -    assert(evdwtype < evdwCuNR);
 +    GMX_ASSERT(eeltype < eelCuNR,
 +               "The electrostatics type requested is not implemented in the CUDA kernels.");
 +    GMX_ASSERT(evdwtype < evdwCuNR,
 +               "The VdW type requested is not implemented in the CUDA kernels.");
 +
 +    /* assert assumptions made by the kernels */
 +    GMX_ASSERT(c_nbnxnGpuClusterSize*c_nbnxnGpuClusterSize/c_nbnxnGpuClusterpairSplit == devInfo->prop.warpSize,
 +               "The CUDA kernels require the cluster_size_i*cluster_size_j/nbnxn_gpu_clusterpair_split to match the warp size of the architecture targeted.");
  
      if (bDoEne)
      {
  }
  
  /*! Calculates the amount of shared memory required by the CUDA kernel in use. */
 -static inline int calc_shmem_required(const int num_threads_z, gmx_device_info_t gmx_unused *dinfo)
 +static inline int calc_shmem_required(const int num_threads_z, gmx_device_info_t gmx_unused *dinfo, const cu_nbparam_t *nbp)
  {
      int shmem;
  
      /* size of shmem (force-buffers/xq/atom type preloading) */
      /* NOTE: with the default kernel on sm3.0 we need shmem only for pre-loading */
      /* i-atom x+q in shared memory */
 -    shmem  = NCL_PER_SUPERCL * CL_SIZE * sizeof(float4);
 +    shmem  = c_numClPerSupercl * c_clSize * sizeof(float4);
      /* cj in shared memory, for each warp separately */
 -    shmem += num_threads_z * 2 * NBNXN_GPU_JGROUP_SIZE * sizeof(int);
 -    /* CUDA versions below 4.2 won't generate code for sm>=3.0 */
 -#if GMX_CUDA_VERSION >= 4200
 +    shmem += num_threads_z * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize * sizeof(int);
      if (dinfo->prop.major >= 3)
      {
 -        /* i-atom types in shared memory */
 -        shmem += NCL_PER_SUPERCL * CL_SIZE * sizeof(int);
 +        if (nbp->vdwtype == evdwCuCUTCOMBGEOM ||
 +            nbp->vdwtype == evdwCuCUTCOMBLB)
 +        {
 +            /* i-atom LJ combination parameters in shared memory */
 +            shmem += c_numClPerSupercl * c_clSize * sizeof(float2);
 +        }
 +        else
 +        {
 +            /* i-atom types in shared memory */
 +            shmem += c_numClPerSupercl * c_clSize * sizeof(int);
 +        }
      }
      if (dinfo->prop.major < 3)
 -#endif
      {
          /* force reduction buffers in shared memory */
 -        shmem += CL_SIZE * CL_SIZE * 3 * sizeof(float);
 +        shmem += c_clSize * c_clSize * 3 * sizeof(float);
      }
      return shmem;
  }
@@@ -414,8 -394,7 +415,8 @@@ void nbnxn_gpu_launch_kernel(gmx_nbnxn_
      nb_kernel = select_nbnxn_kernel(nbp->eeltype,
                                      nbp->vdwtype,
                                      bCalcEner,
 -                                    plist->bDoPrune || always_prune);
 +                                    plist->bDoPrune || always_prune,
 +                                    nb->dev_info);
  
      /* Kernel launch config:
       * - The thread block dimensions match the size of i-clusters, j-clusters,
       * - The 1D block-grid contains as many blocks as super-clusters.
       */
      int num_threads_z = 1;
-     if (nb->dev_info->prop.major == 3 && nb->dev_info->prop.minor == 7)
+     if ((nb->dev_info->prop.major == 3 && nb->dev_info->prop.minor == 7) ||
+         (nb->dev_info->prop.major == 6 && nb->dev_info->prop.minor == 0))
      {
          num_threads_z = 2;
      }
      nblock    = calc_nb_kernel_nblock(plist->nsci, nb->dev_info);
 -    dim_block = dim3(CL_SIZE, CL_SIZE, num_threads_z);
 +    dim_block = dim3(c_clSize, c_clSize, num_threads_z);
      dim_grid  = dim3(nblock, 1, 1);
 -    shmem     = calc_shmem_required(num_threads_z, nb->dev_info);
 +    shmem     = calc_shmem_required(num_threads_z, nb->dev_info, nbp);
  
      if (debug)
      {
                  "\tGrid: %dx%d\n\t#Super-clusters/clusters: %d/%d (%d)\n"
                  "\tShMem: %d\n",
                  dim_block.x, dim_block.y, dim_block.z,
 -                dim_grid.x, dim_grid.y, plist->nsci*NCL_PER_SUPERCL,
 -                NCL_PER_SUPERCL, plist->na_c,
 +                dim_grid.x, dim_grid.y, plist->nsci*c_numClPerSupercl,
 +                c_numClPerSupercl, plist->na_c,
                  shmem);
      }
  
 -    nb_kernel<<< dim_grid, dim_block, shmem, stream>>> (*adat, *nbp, *plist, bCalcFshift);
 +    if (bUseCudaLaunchKernel)
 +    {
 +        gmx_unused void* kernel_args[4];
 +        kernel_args[0] = adat;
 +        kernel_args[1] = nbp;
 +        kernel_args[2] = plist;
 +        kernel_args[3] = &bCalcFshift;
 +
 +#if GMX_CUDA_VERSION >= 7000
 +        cudaLaunchKernel((void *)nb_kernel, dim_grid, dim_block, kernel_args, shmem, stream);
 +#endif
 +    }
 +    else
 +    {
 +        nb_kernel<<< dim_grid, dim_block, shmem, stream>>> (*adat, *nbp, *plist, bCalcFshift);
 +    }
      CU_LAUNCH_ERR("k_calc_nb");
  
      if (bDoTime)
@@@ -479,7 -444,7 +481,7 @@@ void nbnxn_gpu_launch_cpyback(gmx_nbnxn
                                int                     aloc)
  {
      cudaError_t stat;
 -    int         adat_begin, adat_len, adat_end; /* local/nonlocal offset and length used for xq and f */
 +    int         adat_begin, adat_len; /* local/nonlocal offset and length used for xq and f */
      int         iloc = -1;
  
      /* determine interaction locality from atom locality */
      {
          adat_begin  = 0;
          adat_len    = adat->natoms_local;
 -        adat_end    = nb->atdat->natoms_local;
      }
      else
      {
          adat_begin  = adat->natoms_local;
          adat_len    = adat->natoms - adat->natoms_local;
 -        adat_end    = nb->atdat->natoms;
      }
  
      /* beginning of timed D2H section */
          CU_RET_ERR(stat, "cudaEventRecord failed");
      }
  
 -    if (!nb->bUseStreamSync)
 -    {
 -        /* For safety reasons set a few (5%) forces to NaN. This way even if the
 -           polling "hack" fails with some future NVIDIA driver we'll get a crash. */
 -        for (int i = adat_begin; i < 3*adat_end + 2; i += adat_len/20)
 -        {
 -#ifdef NAN
 -            nbatom->out[0].f[i] = NAN;
 -#else
 -#  ifdef _MSVC
 -            if (numeric_limits<float>::has_quiet_NaN)
 -            {
 -                nbatom->out[0].f[i] = numeric_limits<float>::quiet_NaN();
 -            }
 -            else
 -#  endif
 -            {
 -                nbatom->out[0].f[i] = GMX_REAL_MAX;
 -            }
 -#endif
 -        }
 -
 -        /* Set the last four bytes of the force array to a bit pattern
 -           which can't be the result of the force calculation:
 -           max exponent (127) and zero mantissa. */
 -        *(unsigned int*)&nbatom->out[0].f[adat_end*3 - 1] = poll_wait_pattern;
 -    }
 -
      /* With DD the local D2H transfer can only start after the non-local
         kernel has finished. */
      if (iloc == eintLocal && nb->bUseTwoStreams)
      }
  }
  
 -/* Atomic compare-exchange operation on unsigned values. It is used in
 - * polling wait for the GPU.
 - */
 -static inline bool atomic_cas(volatile unsigned int *ptr,
 -                              unsigned int           oldval,
 -                              unsigned int           newval)
 -{
 -    assert(ptr);
 -
 -#ifdef TMPI_ATOMICS
 -    return tMPI_Atomic_cas((tMPI_Atomic_t *)ptr, oldval, newval);
 -#else
 -    gmx_incons("Atomic operations not available, atomic_cas() should not have been called!");
 -    return true;
 -#endif
 -}
 -
  void nbnxn_gpu_wait_for_gpu(gmx_nbnxn_cuda_t *nb,
 -                            const nbnxn_atomdata_t *nbatom,
                              int flags, int aloc,
                              real *e_lj, real *e_el, rvec *fshift)
  {
      /* NOTE:  only implemented for single-precision at this time */
 -    cudaError_t            stat;
 -    int                    i, adat_end, iloc = -1;
 -    volatile unsigned int *poll_word;
 +    cudaError_t stat;
 +    int         iloc = -1;
  
      /* determine interaction locality from atom locality */
      if (LOCAL_A(aloc))
          return;
      }
  
 -    /* calculate the atom data index range based on locality */
 -    if (LOCAL_A(aloc))
 -    {
 -        adat_end = nb->atdat->natoms_local;
 -    }
 -    else
 -    {
 -        adat_end = nb->atdat->natoms;
 -    }
 -
 -    if (nb->bUseStreamSync)
 -    {
 -        stat = cudaStreamSynchronize(nb->stream[iloc]);
 -        CU_RET_ERR(stat, "cudaStreamSynchronize failed in cu_blockwait_nb");
 -    }
 -    else
 -    {
 -        /* Busy-wait until we get the signal pattern set in last byte
 -         * of the l/nl float vector. This pattern corresponds to a floating
 -         * point number which can't be the result of the force calculation
 -         * (maximum, 127 exponent and 0 mantissa).
 -         * The polling uses atomic compare-exchange.
 -         */
 -        poll_word = (volatile unsigned int*)&nbatom->out[0].f[adat_end*3 - 1];
 -        while (atomic_cas(poll_word, poll_wait_pattern, poll_wait_pattern))
 -        {
 -        }
 -    }
 +    stat = cudaStreamSynchronize(nb->stream[iloc]);
 +    CU_RET_ERR(stat, "cudaStreamSynchronize failed in cu_blockwait_nb");
  
      /* timing data accumulation */
      if (nb->bDoTime)
  
          if (bCalcFshift)
          {
 -            for (i = 0; i < SHIFTS; i++)
 +            for (int i = 0; i < SHIFTS; i++)
              {
                  fshift[i][0] += nbst.fshift[i].x;
                  fshift[i][1] += nbst.fshift[i].y;
@@@ -722,11 -762,11 +724,11 @@@ void nbnxn_cuda_set_cacheconfig(gmx_dev
          {
              if (devinfo->prop.major >= 3)
              {
 -                /* Default kernel on sm 3.x 48/16 kB Shared/L1 */
 -                cudaFuncSetCacheConfig(nb_kfunc_ener_prune_ptr[i][j], cudaFuncCachePreferShared);
 -                cudaFuncSetCacheConfig(nb_kfunc_ener_noprune_ptr[i][j], cudaFuncCachePreferShared);
 -                cudaFuncSetCacheConfig(nb_kfunc_noener_prune_ptr[i][j], cudaFuncCachePreferShared);
 -                stat = cudaFuncSetCacheConfig(nb_kfunc_noener_noprune_ptr[i][j], cudaFuncCachePreferShared);
 +                /* Default kernel on sm 3.x and later 32/32 kB Shared/L1 */
 +                cudaFuncSetCacheConfig(nb_kfunc_ener_prune_ptr[i][j], cudaFuncCachePreferEqual);
 +                cudaFuncSetCacheConfig(nb_kfunc_ener_noprune_ptr[i][j], cudaFuncCachePreferEqual);
 +                cudaFuncSetCacheConfig(nb_kfunc_noener_prune_ptr[i][j], cudaFuncCachePreferEqual);
 +                stat = cudaFuncSetCacheConfig(nb_kfunc_noener_noprune_ptr[i][j], cudaFuncCachePreferEqual);
              }
              else
              {
index c63eda7f43bbb1f761d06018c4570a26482ea6a9,91ab0c6967edb70d9d8f45ca218041ef69ee88ae..de26e947b75d943ae4ac337b55e3b325a726f5c6
   *  NOTE: No include fence as it is meant to be included multiple times.
   *
   *  \author Szilárd Páll <pall.szilard@gmail.com>
 + *  \author Berk Hess <hess@kth.se>
   *  \ingroup module_mdlib
   */
 -#include "config.h"
  
 +#include "gromacs/gpu_utils/cuda_arch_utils.cuh"
  #include "gromacs/math/utilities.h"
  #include "gromacs/pbcutil/ishift.h"
  /* Note that floating-point constants in CUDA code should be suffixed
   * code that is in double precision.
   */
  
 -#if __CUDA_ARCH__ >= 300
 -/* Note: convenience macros, need to be undef-ed at the end of the file. */
 -#define REDUCE_SHUFFLE
 -/* On Kepler pre-loading i-atom types to shmem gives a few %,
 -   but on Fermi it does not */
 -#define IATYPE_SHMEM
 -#ifdef HAVE_CUDA_TEXOBJ_SUPPORT
 -#define USE_TEXOBJ
 -#endif
 +#if GMX_PTX_ARCH < 300
 +#error "nbnxn_cuda_kernel.cuh included with GMX_PTX_ARCH < 300"
  #endif
  
  #if defined EL_EWALD_ANA || defined EL_EWALD_TAB
  #define LJ_EWALD
  #endif
  
 +#if defined LJ_COMB_GEOM || defined LJ_COMB_LB
 +#define LJ_COMB
 +#endif
  
  /*
     Kernel launch parameters:
      - #blocks   = #pair lists, blockId = pair list Id
 -    - #threads  = NTHREAD_Z * CL_SIZE^2
 +    - #threads  = NTHREAD_Z * c_clSize^2
      - shmem     = see nbnxn_cuda.cu:calc_shmem_required()
  
      Each thread calculates an i force-component taking one pair of i-j atoms.
   */
  
 -/* NTHREAD_Z controls the number of j-clusters processed concurrently on NTHREAD_Z
 +/**@{*/
 +/*! \brief Compute capability dependent definition of kernel launch configuration parameters.
 + *
 + * NTHREAD_Z controls the number of j-clusters processed concurrently on NTHREAD_Z
   * warp-pairs per block.
   *
   * - On CC 2.0-3.5, 5.0, and 5.2, NTHREAD_Z == 1, translating to 64 th/block with 16
   * NTHREAD_Z > 1 results in excessive register spilling unless the minimum blocks
   * per multiprocessor is reduced proportionally to get the original number of max
   * threads in flight (and slightly lower performance).
-  * - On CC 3.7 there are enough registers to double the number of threads; using
+  * - On CC 3.7 and 6.0 there are enough registers to double the number of threads; using
   * NTHREADS_Z == 2 is fastest with 16 blocks (TODO: test with RF and other kernels
   * with low-register use).
   *
   * shuffle-based reduction, hence CC >= 3.0.
   */
  
 -/* Kernel launch bounds as function of NTHREAD_Z.
 +/* Kernel launch bounds for different compute capabilities. The value of NTHREAD_Z
 + * determines the number of threads per block and it is chosen such that
 + * 16 blocks/multiprocessor can be kept in flight.
-  * - CC 2.x, 3.0, 3.5, 5.x: NTHREAD_Z=1, (64, 16) bounds
-  * - CC 3.7:                NTHREAD_Z=2, (128, 16) bounds
+  * - CC 3.0/3.5/5.x, >=6.1: NTHREAD_Z=1, (64, 16) bounds
+  * - CC 3.7, 6.0:           NTHREAD_Z=2, (128, 16) bounds
+  *
+  * Note: convenience macros, need to be undef-ed at the end of the file.
   */
- #if GMX_PTX_ARCH == 370
 -#if __CUDA_ARCH__ == 370 || __CUDA_ARCH__ == 600
 -#define NTHREAD_Z           (2)
 -#define MIN_BLOCKS_PER_MP   (16)
++#if GMX_PTX_ARCH == 370 || GMX_PTX_ARCH == 600
 +    #define NTHREAD_Z           (2)
 +    #define MIN_BLOCKS_PER_MP   (16)
  #else
 -#define NTHREAD_Z           (1)
 -#define MIN_BLOCKS_PER_MP   (16)
 -#endif
 -#define THREADS_PER_BLOCK   (CL_SIZE*CL_SIZE*NTHREAD_Z)
 +    #define NTHREAD_Z           (1)
 +    #define MIN_BLOCKS_PER_MP   (16)
- #endif /* GMX_PTX_ARCH == 370 */
++#endif /* GMX_PTX_ARCH == 370 || GMX_PTX_ARCH == 600 */
 +#define THREADS_PER_BLOCK   (c_clSize*c_clSize*NTHREAD_Z)
  
 -#if __CUDA_ARCH__ >= 350
 +#if GMX_PTX_ARCH >= 350
 +#if (GMX_PTX_ARCH <= 210) && (NTHREAD_Z > 1)
 +    #error NTHREAD_Z > 1 will give incorrect results on CC 2.x
 +#endif
 +/**@}*/
  __launch_bounds__(THREADS_PER_BLOCK, MIN_BLOCKS_PER_MP)
  #else
  __launch_bounds__(THREADS_PER_BLOCK)
 -#endif
 +#endif /* GMX_PTX_ARCH >= 350 */
  #ifdef PRUNE_NBL
  #ifdef CALC_ENERGIES
  __global__ void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _VF_prune_cuda)
  #else
  __global__ void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_prune_cuda)
 -#endif
 +#endif /* CALC_ENERGIES */
  #else
  #ifdef CALC_ENERGIES
  __global__ void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _VF_cuda)
  #else
  __global__ void NB_KERNEL_FUNC_NAME(nbnxn_kernel, _F_cuda)
 -#endif
 -#endif
 +#endif /* CALC_ENERGIES */
 +#endif /* PRUNE_NBL */
  (const cu_atomdata_t atdat,
   const cu_nbparam_t nbparam,
   const cu_plist_t plist,
   bool bCalcFshift)
 +#ifdef FUNCTION_DECLARATION_ONLY
 +;     /* Only do function declaration, omit the function body. */
 +#else
  {
      /* convenience variables */
      const nbnxn_sci_t *pl_sci       = plist.sci;
  #endif
      nbnxn_cj4_t        *pl_cj4      = plist.cj4;
      const nbnxn_excl_t *excl        = plist.excl;
 +#ifndef LJ_COMB
      const int          *atom_types  = atdat.atom_types;
      int                 ntypes      = atdat.ntypes;
 +#else
 +    const float2       *lj_comb     = atdat.lj_comb;
 +    float2              ljcp_i, ljcp_j;
 +#endif
      const float4       *xq          = atdat.xq;
      float3             *f           = atdat.f;
      const float3       *shift_vec   = atdat.shift_vec;
      unsigned int tidxz  = threadIdx.z;
  #endif
      unsigned int bidx   = blockIdx.x;
 -    unsigned int widx   = tidx / WARP_SIZE; /* warp index */
 +    unsigned int widx   = tidx / warp_size; /* warp index */
  
 -    int          sci, ci, cj, ci_offset,
 +    int          sci, ci, cj,
                   ai, aj,
 -                 cij4_start, cij4_end,
 -                 typei, typej,
 -                 i, jm, j4, wexcl_idx;
 +                 cij4_start, cij4_end;
 +#ifndef LJ_COMB
 +    int          typei, typej;
 +#endif
 +    int          i, jm, j4, wexcl_idx;
      float        qi, qj_f,
 -                 r2, inv_r, inv_r2, inv_r6,
 -                 c6, c12,
 -                 int_bit,
 +                 r2, inv_r, inv_r2;
 +#if !defined LJ_COMB_LB || defined CALC_ENERGIES
 +    float        inv_r6, c6, c12;
 +#endif
 +#ifdef LJ_COMB_LB
 +    float        sigma, epsilon;
 +#endif
 +    float        int_bit,
                   F_invr;
  #ifdef CALC_ENERGIES
      float        E_lj, E_el;
      unsigned int wexcl, imask, mask_ji;
      float4       xqbuf;
      float3       xi, xj, rv, f_ij, fcj_buf;
 -    float3       fci_buf[NCL_PER_SUPERCL]; /* i force buffer */
 +    float3       fci_buf[c_numClPerSupercl]; /* i force buffer */
      nbnxn_sci_t  nb_sci;
  
 +    /*! i-cluster interaction mask for a super-cluster with all c_numClPerSupercl=8 bits set */
 +    const unsigned superClInteractionMask = ((1U << c_numClPerSupercl) - 1U);
 +
      /* shmem buffer for i x+q pre-loading */
      extern __shared__  float4 xqib[];
 +
      /* shmem buffer for cj, for each warp separately */
 -    int *cjs     = ((int *)(xqib + NCL_PER_SUPERCL * CL_SIZE)) + tidxz * 2 * NBNXN_GPU_JGROUP_SIZE;
 -#ifdef IATYPE_SHMEM
 +    int *cjs       = ((int *)(xqib + c_numClPerSupercl * c_clSize)) + tidxz * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize;
 +    int *cjs_end   = ((int *)(xqib + c_numClPerSupercl * c_clSize)) + NTHREAD_Z * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize;
 +#ifndef LJ_COMB
      /* shmem buffer for i atom-type pre-loading */
 -    int *atib    = ((int *)(xqib + NCL_PER_SUPERCL * CL_SIZE)) + NTHREAD_Z * 2 * NBNXN_GPU_JGROUP_SIZE;
 -#endif
 -
 -#ifndef REDUCE_SHUFFLE
 -    /* shmem j force buffer */
 -#ifdef IATYPE_SHMEM
 -    float *f_buf = (float *)(atib + NCL_PER_SUPERCL * CL_SIZE);
 +    int *atib      = cjs_end;
  #else
 -    float *f_buf = (float *)(cjs + NTHREAD_Z * 2 * NBNXN_GPU_JGROUP_SIZE);
 -#endif
 +    /* shmem buffer for i-atom LJ combination rule parameters */
 +    float2 *ljcpib = (float2 *)cjs_end;
  #endif
  
      nb_sci      = pl_sci[bidx];         /* my i super-cluster's index = current bidx */
      if (tidxz == 0)
      {
          /* Pre-load i-atom x and q into shared memory */
 -        ci = sci * NCL_PER_SUPERCL + tidxj;
 -        ai = ci * CL_SIZE + tidxi;
 -        xqib[tidxj * CL_SIZE + tidxi] = xq[ai] + shift_vec[nb_sci.shift];
 -#ifdef IATYPE_SHMEM
 +        ci = sci * c_numClPerSupercl + tidxj;
 +        ai = ci * c_clSize + tidxi;
 +
 +        xqbuf    = xq[ai] + shift_vec[nb_sci.shift];
 +        xqbuf.w *= nbparam.epsfac;
 +        xqib[tidxj * c_clSize + tidxi] = xqbuf;
 +
 +#ifndef LJ_COMB
          /* Pre-load the i-atom types into shared memory */
 -        atib[tidxj * CL_SIZE + tidxi] = atom_types[ai];
 +        atib[tidxj * c_clSize + tidxi] = atom_types[ai];
 +#else
 +        /* Pre-load the LJ combination parameters into shared memory */
 +        ljcpib[tidxj * c_clSize + tidxi] = lj_comb[ai];
  #endif
      }
      __syncthreads();
  
 -    for (ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
 +    for (i = 0; i < c_numClPerSupercl; i++)
      {
 -        fci_buf[ci_offset] = make_float3(0.0f);
 +        fci_buf[i] = make_float3(0.0f);
      }
  
  #ifdef LJ_EWALD
      /* TODO: we are trading registers with flops by keeping lje_coeff-s, try re-calculating it later */
      lje_coeff2   = nbparam.ewaldcoeff_lj*nbparam.ewaldcoeff_lj;
 -    lje_coeff6_6 = lje_coeff2*lje_coeff2*lje_coeff2*ONE_SIXTH_F;
 -#endif /* LJ_EWALD */
 +    lje_coeff6_6 = lje_coeff2*lje_coeff2*lje_coeff2*c_oneSixth;
 +#endif
  
  
  #ifdef CALC_ENERGIES
      E_lj = 0.0f;
      E_el = 0.0f;
  
 -#if defined EXCLUSION_FORCES /* Ewald or RF */
 -    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci*NCL_PER_SUPERCL)
 +#ifdef EXCLUSION_FORCES /* Ewald or RF */
 +    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci*c_numClPerSupercl)
      {
          /* we have the diagonal: add the charge and LJ self interaction energy term */
 -        for (i = 0; i < NCL_PER_SUPERCL; i++)
 +        for (i = 0; i < c_numClPerSupercl; i++)
          {
  #if defined EL_EWALD_ANY || defined EL_RF || defined EL_CUTOFF
 -            qi    = xqib[i * CL_SIZE + tidxi].w;
 +            qi    = xqib[i * c_clSize + tidxi].w;
              E_el += qi*qi;
  #endif
  
 -#if defined LJ_EWALD
 -#ifdef USE_TEXOBJ
 -            E_lj += tex1Dfetch<float>(nbparam.nbfp_texobj, atom_types[(sci*NCL_PER_SUPERCL + i)*CL_SIZE + tidxi]*(ntypes + 1)*2);
 -#else
 -            E_lj += tex1Dfetch(nbfp_texref, atom_types[(sci*NCL_PER_SUPERCL + i)*CL_SIZE + tidxi]*(ntypes + 1)*2);
 -#endif /* USE_TEXOBJ */
 -#endif /* LJ_EWALD */
 -
 +#ifdef LJ_EWALD
 +            E_lj += tex1Dfetch<float>(nbparam.nbfp_texobj, atom_types[(sci*c_numClPerSupercl + i)*c_clSize + tidxi]*(ntypes + 1)*2);
 +#endif
          }
  
          /* divide the self term(s) equally over the j-threads, then multiply with the coefficients. */
  #ifdef LJ_EWALD
 -        E_lj /= CL_SIZE*NTHREAD_Z;
 -        E_lj *= 0.5f*ONE_SIXTH_F*lje_coeff6_6;
 -#endif  /* LJ_EWALD */
 +        E_lj /= c_clSize*NTHREAD_Z;
 +        E_lj *= 0.5f*c_oneSixth*lje_coeff6_6;
 +#endif
  
  #if defined EL_EWALD_ANY || defined EL_RF || defined EL_CUTOFF
 -        E_el /= CL_SIZE*NTHREAD_Z;
 +        /* Correct for epsfac^2 due to adding qi^2 */
 +        E_el /= nbparam.epsfac*c_clSize*NTHREAD_Z;
  #if defined EL_RF || defined EL_CUTOFF
 -        E_el *= -nbparam.epsfac*0.5f*c_rf;
 +        E_el *= -0.5f*c_rf;
  #else
 -        E_el *= -nbparam.epsfac*beta*M_FLOAT_1_SQRTPI; /* last factor 1/sqrt(pi) */
 +        E_el *= -beta*M_FLOAT_1_SQRTPI; /* last factor 1/sqrt(pi) */
  #endif
 -#endif                                                 /* EL_EWALD_ANY || defined EL_RF || defined EL_CUTOFF */
 +#endif                                  /* EL_EWALD_ANY || defined EL_RF || defined EL_CUTOFF */
      }
 -#endif                                                 /* EXCLUSION_FORCES */
 -
 -#endif                                                 /* CALC_ENERGIES */
 +#endif                                  /* EXCLUSION_FORCES */
  
 -    /* skip central shifts when summing shift forces */
 -    if (nb_sci.shift == CENTRAL)
 -    {
 -        bCalcFshift = false;
 -    }
 +#endif                                  /* CALC_ENERGIES */
  
      /* loop over the j clusters = seen by any of the atoms in the current super-cluster */
      for (j4 = cij4_start + tidxz; j4 < cij4_end; j4 += NTHREAD_Z)
      {
          wexcl_idx   = pl_cj4[j4].imei[widx].excl_ind;
          imask       = pl_cj4[j4].imei[widx].imask;
 -        wexcl       = excl[wexcl_idx].pair[(tidx) & (WARP_SIZE - 1)];
 +        wexcl       = excl[wexcl_idx].pair[(tidx) & (warp_size - 1)];
  
  #ifndef PRUNE_NBL
          if (imask)
  #endif
          {
              /* Pre-load cj into shared memory on both warps separately */
 -            if ((tidxj == 0 || tidxj == 4) && tidxi < NBNXN_GPU_JGROUP_SIZE)
 +            if ((tidxj == 0 || tidxj == 4) && tidxi < c_nbnxnGpuJgroupSize)
              {
 -                cjs[tidxi + tidxj * NBNXN_GPU_JGROUP_SIZE / 4] = pl_cj4[j4].cj[tidxi];
 +                cjs[tidxi + tidxj * c_nbnxnGpuJgroupSize/c_splitClSize] = pl_cj4[j4].cj[tidxi];
              }
  
              /* Unrolling this loop
                 - with pruning leads to register spilling;
 -               - on Kepler is much slower;
 -               - doesn't work on CUDA <v4.1
 -               Tested with nvcc 3.2 - 5.0.7 */
 -#if !defined PRUNE_NBL && __CUDA_ARCH__ < 300 && GMX_CUDA_VERSION >= 4010
 -#pragma unroll 4
 -#endif
 -            for (jm = 0; jm < NBNXN_GPU_JGROUP_SIZE; jm++)
 +               - on Kepler and later it is much slower;
 +               Tested with up to nvcc 7.5 */
 +            for (jm = 0; jm < c_nbnxnGpuJgroupSize; jm++)
              {
 -                /* ((1U << NCL_PER_SUPERCL) - 1U) is the i-cluster interaction
 -                 * mask for a super-cluster with all NCL_PER_SUPERCL bits set.
 -                 */
 -                if (imask & (((1U << NCL_PER_SUPERCL) - 1U) << (jm * NCL_PER_SUPERCL)))
 +                if (imask & (superClInteractionMask << (jm * c_numClPerSupercl)))
                  {
 -                    mask_ji = (1U << (jm * NCL_PER_SUPERCL));
 +                    mask_ji = (1U << (jm * c_numClPerSupercl));
  
 -                    cj      = cjs[jm + (tidxj & 4) * NBNXN_GPU_JGROUP_SIZE / 4];
 -                    aj      = cj * CL_SIZE + tidxj;
 +                    cj      = cjs[jm + (tidxj & 4) * c_nbnxnGpuJgroupSize/c_splitClSize];
 +                    aj      = cj * c_clSize + tidxj;
  
                      /* load j atom data */
                      xqbuf   = xq[aj];
                      xj      = make_float3(xqbuf.x, xqbuf.y, xqbuf.z);
 -                    qj_f    = nbparam.epsfac * xqbuf.w;
 +                    qj_f    = xqbuf.w;
 +#ifndef LJ_COMB
                      typej   = atom_types[aj];
 +#else
 +                    ljcp_j  = lj_comb[aj];
 +#endif
  
                      fcj_buf = make_float3(0.0f);
  
 -                    /* The PME and RF kernels don't unroll with CUDA <v4.1. */
 -#if !defined PRUNE_NBL && !(GMX_CUDA_VERSION < 4010 && defined EXCLUSION_FORCES)
 +#if !defined PRUNE_NBL
  #pragma unroll 8
  #endif
 -                    for (i = 0; i < NCL_PER_SUPERCL; i++)
 +                    for (i = 0; i < c_numClPerSupercl; i++)
                      {
                          if (imask & mask_ji)
                          {
 -                            ci_offset   = i;                     /* i force buffer offset */
 -
 -                            ci      = sci * NCL_PER_SUPERCL + i; /* i cluster index */
 -                            ai      = ci * CL_SIZE + tidxi;      /* i atom index */
 +                            ci      = sci * c_numClPerSupercl + i; /* i cluster index */
  
                              /* all threads load an atom from i cluster ci into shmem! */
 -                            xqbuf   = xqib[i * CL_SIZE + tidxi];
 +                            xqbuf   = xqib[i * c_clSize + tidxi];
                              xi      = make_float3(xqbuf.x, xqbuf.y, xqbuf.z);
  
                              /* distance between i and j atoms */
                              {
                                  /* load the rest of the i-atom parameters */
                                  qi      = xqbuf.w;
 -#ifdef IATYPE_SHMEM
 -                                typei   = atib[i * CL_SIZE + tidxi];
 -#else
 -                                typei   = atom_types[ai];
 -#endif
  
 +#ifndef LJ_COMB
                                  /* LJ 6*C6 and 12*C12 */
 -#ifdef USE_TEXOBJ
 +                                typei   = atib[i * c_clSize + tidxi];
                                  c6      = tex1Dfetch<float>(nbparam.nbfp_texobj, 2 * (ntypes * typei + typej));
                                  c12     = tex1Dfetch<float>(nbparam.nbfp_texobj, 2 * (ntypes * typei + typej) + 1);
  #else
 -                                c6      = tex1Dfetch(nbfp_texref, 2 * (ntypes * typei + typej));
 -                                c12     = tex1Dfetch(nbfp_texref, 2 * (ntypes * typei + typej) + 1);
 -#endif                          /* USE_TEXOBJ */
 -
 +                                ljcp_i  = ljcpib[i * c_clSize + tidxi];
 +#ifdef LJ_COMB_GEOM
 +                                c6      = ljcp_i.x * ljcp_j.x;
 +                                c12     = ljcp_i.y * ljcp_j.y;
 +#else
 +                                /* LJ 2^(1/6)*sigma and 12*epsilon */
 +                                sigma   = ljcp_i.x + ljcp_j.x;
 +                                epsilon = ljcp_i.y * ljcp_j.y;
 +#if defined CALC_ENERGIES || defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
 +                                convert_sigma_epsilon_to_c6_c12(sigma, epsilon, &c6, &c12);
 +#endif
 +#endif                          /* LJ_COMB_GEOM */
 +#endif                          /* LJ_COMB */
  
 -                                /* avoid NaN for excluded pairs at r=0 */
 -                                r2      += (1.0f - int_bit) * NBNXN_AVOID_SING_R2_INC;
 +                                // Ensure distance do not become so small that r^-12 overflows
 +                                r2      = max(r2, NBNXN_MIN_RSQ);
  
                                  inv_r   = rsqrt(r2);
                                  inv_r2  = inv_r * inv_r;
 +#if !defined LJ_COMB_LB || defined CALC_ENERGIES
                                  inv_r6  = inv_r2 * inv_r2 * inv_r2;
 -#if defined EXCLUSION_FORCES
 +#ifdef EXCLUSION_FORCES
                                  /* We could mask inv_r2, but with Ewald
                                   * masking both inv_r6 and F_invr is faster */
                                  inv_r6  *= int_bit;
  
                                  F_invr  = inv_r6 * (c12 * inv_r6 - c6) * inv_r2;
  #if defined CALC_ENERGIES || defined LJ_POT_SWITCH
 -                                E_lj_p  = int_bit * (c12 * (inv_r6 * inv_r6 + nbparam.repulsion_shift.cpot)*ONE_TWELVETH_F -
 -                                                     c6 * (inv_r6 + nbparam.dispersion_shift.cpot)*ONE_SIXTH_F);
 +                                E_lj_p  = int_bit * (c12 * (inv_r6 * inv_r6 + nbparam.repulsion_shift.cpot)*c_oneTwelveth -
 +                                                     c6 * (inv_r6 + nbparam.dispersion_shift.cpot)*c_oneSixth);
  #endif
 +#else                           /* !LJ_COMB_LB || CALC_ENERGIES */
 +                                float sig_r  = sigma*inv_r;
 +                                float sig_r2 = sig_r*sig_r;
 +                                float sig_r6 = sig_r2*sig_r2*sig_r2;
 +#ifdef EXCLUSION_FORCES
 +                                sig_r6 *= int_bit;
 +#endif                          /* EXCLUSION_FORCES */
 +
 +                                F_invr  = epsilon * sig_r6 * (sig_r6 - 1.0f) * inv_r2;
 +#endif                          /* !LJ_COMB_LB || CALC_ENERGIES */
  
  #ifdef LJ_FORCE_SWITCH
  #ifdef CALC_ENERGIES
                                  F_invr  += qi * qj_f * (int_bit*inv_r2*inv_r + pmecorrF(beta2*r2)*beta3);
  #elif defined EL_EWALD_TAB
                                  F_invr  += qi * qj_f * (int_bit*inv_r2 -
 -#ifdef USE_TEXOBJ
 -                                                        interpolate_coulomb_force_r(nbparam.coulomb_tab_texobj, r2 * inv_r, coulomb_tab_scale)
 -#else
 -                                                        interpolate_coulomb_force_r(r2 * inv_r, coulomb_tab_scale)
 -#endif /* USE_TEXOBJ */
 -                                                        ) * inv_r;
 -#endif /* EL_EWALD_ANA/TAB */
 +                                                        interpolate_coulomb_force_r(nbparam.coulomb_tab_texobj, r2 * inv_r, coulomb_tab_scale)) * inv_r;
 +#endif                          /* EL_EWALD_ANA/TAB */
  
  #ifdef CALC_ENERGIES
  #ifdef EL_CUTOFF
                                  fcj_buf -= f_ij;
  
                                  /* accumulate i forces in registers */
 -                                fci_buf[ci_offset] += f_ij;
 +                                fci_buf[i] += f_ij;
                              }
                          }
  
                      }
  
                      /* reduce j forces */
 -#ifdef REDUCE_SHUFFLE
                      reduce_force_j_warp_shfl(fcj_buf, f, tidxi, aj);
 -#else
 -                    /* store j forces in shmem */
 -                    f_buf[                  tidx] = fcj_buf.x;
 -                    f_buf[    FBUF_STRIDE + tidx] = fcj_buf.y;
 -                    f_buf[2 * FBUF_STRIDE + tidx] = fcj_buf.z;
 -
 -                    reduce_force_j_generic(f_buf, f, tidxi, tidxj, aj);
 -#endif
                  }
              }
  #ifdef PRUNE_NBL
          }
      }
  
 +    /* skip central shifts when summing shift forces */
 +    if (nb_sci.shift == CENTRAL)
 +    {
 +        bCalcFshift = false;
 +    }
 +
      float fshift_buf = 0.0f;
  
      /* reduce i forces */
 -    for (ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
 +    for (i = 0; i < c_numClPerSupercl; i++)
      {
 -        ai  = (sci * NCL_PER_SUPERCL + ci_offset) * CL_SIZE + tidxi;
 -#ifdef REDUCE_SHUFFLE
 -        reduce_force_i_warp_shfl(fci_buf[ci_offset], f,
 +        ai  = (sci * c_numClPerSupercl + i) * c_clSize + tidxi;
 +        reduce_force_i_warp_shfl(fci_buf[i], f,
                                   &fshift_buf, bCalcFshift,
                                   tidxj, ai);
 -#else
 -        f_buf[                  tidx] = fci_buf[ci_offset].x;
 -        f_buf[    FBUF_STRIDE + tidx] = fci_buf[ci_offset].y;
 -        f_buf[2 * FBUF_STRIDE + tidx] = fci_buf[ci_offset].z;
 -        __syncthreads();
 -        reduce_force_i(f_buf, f,
 -                       &fshift_buf, bCalcFshift,
 -                       tidxi, tidxj, ai);
 -        __syncthreads();
 -#endif
      }
  
      /* add up local shift forces into global mem, tidxj indexes x,y,z */
 -#ifdef REDUCE_SHUFFLE
      if (bCalcFshift && (tidxj & 3) < 3)
      {
 -        atomicAdd(&(atdat.fshift[nb_sci.shift].x) + (tidxj & ~4), fshift_buf);
 +        atomicAdd(&(atdat.fshift[nb_sci.shift].x) + (tidxj & 3), fshift_buf);
      }
 -#else
 -    if (bCalcFshift && tidxj < 3)
 -    {
 -        atomicAdd(&(atdat.fshift[nb_sci.shift].x) + tidxj, fshift_buf);
 -    }
 -#endif
  
  #ifdef CALC_ENERGIES
 -#ifdef REDUCE_SHUFFLE
      /* reduce the energies over warps and store into global memory */
      reduce_energy_warp_shfl(E_lj, E_el, e_lj, e_el, tidx);
 -#else
 -    /* flush the energies to shmem and reduce them */
 -    f_buf[              tidx] = E_lj;
 -    f_buf[FBUF_STRIDE + tidx] = E_el;
 -    reduce_energy_pow2(f_buf + (tidx & WARP_SIZE), e_lj, e_el, tidx & ~WARP_SIZE);
 -#endif
  #endif
  }
 -
 -#undef REDUCE_SHUFFLE
 -#undef IATYPE_SHMEM
 -#undef USE_TEXOBJ
 +#endif /* FUNCTION_DECLARATION_ONLY */
  
  #undef NTHREAD_Z
  #undef MIN_BLOCKS_PER_MP
  #undef EL_EWALD_ANY
  #undef EXCLUSION_FORCES
  #undef LJ_EWALD
 +
 +#undef LJ_COMB
index cbdd58649317e166a020f09ff0f279731eb7fd4f,58b88b80f32ce293a346c63c62e404262236b063..0a65e04d0b396f68d1ab5fbb81558fe750e8ce76
   */
  #include "gmxpre.h"
  
 -#include "gromacs/legacyheaders/update.h"
 +#include "update.h"
  
  #include <math.h>
  #include <stdio.h>
  
  #include <algorithm>
  
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/fileio/confio.h"
 -#include "gromacs/legacyheaders/constr.h"
 -#include "gromacs/legacyheaders/disre.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/nrnb.h"
 -#include "gromacs/legacyheaders/orires.h"
 -#include "gromacs/legacyheaders/tgroup.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gmxlib/nrnb.h"
 +#include "gromacs/listed-forces/disre.h"
 +#include "gromacs/listed-forces/orires.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/invertmatrix.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/math/vecdump.h"
 +#include "gromacs/mdlib/constr.h"
 +#include "gromacs/mdlib/force.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/mdrun.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdlib/tgroup.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/group.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/pbcutil/boxutilities.h"
  #include "gromacs/pbcutil/mshift.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/pulling/pull.h"
 -#include "gromacs/random/random.h"
 +#include "gromacs/random/tabulatednormaldistribution.h"
 +#include "gromacs/random/threefry.h"
  #include "gromacs/timing/wallcycle.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/futil.h"
 +#include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/gmxomp.h"
  #include "gromacs/utility/smalloc.h"
  
  /*#define STARTFROMDT2*/
  
  typedef struct {
 -    double gdt;
 -    double eph;
 -    double emh;
      double em;
 -    double b;
 -    double c;
 -    double d;
  } gmx_sd_const_t;
  
  typedef struct {
      real V;
 -    real X;
 -    real Yv;
 -    real Yx;
  } gmx_sd_sigma_t;
  
  typedef struct {
      /* SD stuff */
      gmx_sd_const_t *sdc;
      gmx_sd_sigma_t *sdsig;
 -    rvec           *sd_V;
 -    int             sd_V_nalloc;
      /* andersen temperature control stuff */
      gmx_bool       *randomize_group;
      real           *boltzfac;
  } gmx_stochd_t;
  
 -typedef struct gmx_update
 +struct gmx_update_t
  {
      gmx_stochd_t *sd;
      /* xprime for constraint algorithms */
      /* Variables for the deform algorithm */
      gmx_int64_t     deformref_step;
      matrix          deformref_box;
 -} t_gmx_update;
 +};
  
  
  static void do_update_md(int start, int nrend, double dt,
@@@ -272,7 -273,7 +272,7 @@@ static void do_update_vv_vel(int start
      {
          g        = 0.25*dt*veta*alpha;
          mv1      = exp(-g);
 -        mv2      = series_sinhx(g);
 +        mv2      = gmx::series_sinhx(g);
      }
      else
      {
@@@ -320,7 -321,7 +320,7 @@@ static void do_update_vv_pos(int start
      {
          g        = 0.5*dt*veta;
          mr1      = exp(g);
 -        mr2      = series_sinhx(g);
 +        mr2      = gmx::series_sinhx(g);
      }
      else
      {
@@@ -457,14 -458,16 +457,14 @@@ static void do_update_visc(int start, i
      }
  }
  
 -static gmx_stochd_t *init_stochd(t_inputrec *ir)
 +static gmx_stochd_t *init_stochd(const t_inputrec *ir)
  {
      gmx_stochd_t   *sd;
 -    gmx_sd_const_t *sdc;
 -    int             ngtc, n;
 -    real            y;
  
      snew(sd, 1);
  
 -    ngtc = ir->opts.ngtc;
 +    const t_grpopts *opts = &ir->opts;
 +    int              ngtc = opts->ngtc;
  
      if (ir->eI == eiBD)
      {
          snew(sd->sdc, ngtc);
          snew(sd->sdsig, ngtc);
  
 -        sdc = sd->sdc;
 -        for (n = 0; n < ngtc; n++)
 +        gmx_sd_const_t *sdc = sd->sdc;
 +
 +        for (int gt = 0; gt < ngtc; gt++)
          {
 -            if (ir->opts.tau_t[n] > 0)
 +            if (opts->tau_t[gt] > 0)
              {
 -                sdc[n].gdt = ir->delta_t/ir->opts.tau_t[n];
 -                sdc[n].eph = exp(sdc[n].gdt/2);
 -                sdc[n].emh = exp(-sdc[n].gdt/2);
 -                sdc[n].em  = exp(-sdc[n].gdt);
 +                sdc[gt].em  = exp(-ir->delta_t/opts->tau_t[gt]);
              }
              else
              {
                  /* No friction and noise on this group */
 -                sdc[n].gdt = 0;
 -                sdc[n].eph = 1;
 -                sdc[n].emh = 1;
 -                sdc[n].em  = 1;
 -            }
 -            if (sdc[n].gdt >= 0.05)
 -            {
 -                sdc[n].b = sdc[n].gdt*(sdc[n].eph*sdc[n].eph - 1)
 -                    - 4*(sdc[n].eph - 1)*(sdc[n].eph - 1);
 -                sdc[n].c = sdc[n].gdt - 3 + 4*sdc[n].emh - sdc[n].em;
 -                sdc[n].d = 2 - sdc[n].eph - sdc[n].emh;
 -            }
 -            else
 -            {
 -                y = sdc[n].gdt/2;
 -                /* Seventh order expansions for small y */
 -                sdc[n].b = y*y*y*y*(1/3.0+y*(1/3.0+y*(17/90.0+y*7/9.0)));
 -                sdc[n].c = y*y*y*(2/3.0+y*(-1/2.0+y*(7/30.0+y*(-1/12.0+y*31/1260.0))));
 -                sdc[n].d = y*y*(-1+y*y*(-1/12.0-y*y/360.0));
 -            }
 -            if (debug)
 -            {
 -                fprintf(debug, "SD const tc-grp %d: b %g  c %g  d %g\n",
 -                        n, sdc[n].b, sdc[n].c, sdc[n].d);
 +                sdc[gt].em  = 1;
              }
          }
      }
      else if (ETC_ANDERSEN(ir->etc))
      {
 -        int        ngtc;
 -        t_grpopts *opts;
 -        real       reft;
 -
 -        opts = &ir->opts;
 -        ngtc = opts->ngtc;
 -
          snew(sd->randomize_group, ngtc);
          snew(sd->boltzfac, ngtc);
  
          /* for now, assume that all groups, if randomized, are randomized at the same rate, i.e. tau_t is the same. */
          /* since constraint groups don't necessarily match up with temperature groups! This is checked in readir.c */
  
 -        for (n = 0; n < ngtc; n++)
 +        for (int gt = 0; gt < ngtc; gt++)
          {
 -            reft = std::max<real>(0, opts->ref_t[n]);
 -            if ((opts->tau_t[n] > 0) && (reft > 0))  /* tau_t or ref_t = 0 means that no randomization is done */
 +            real reft = std::max<real>(0, opts->ref_t[gt]);
 +            if ((opts->tau_t[gt] > 0) && (reft > 0))  /* tau_t or ref_t = 0 means that no randomization is done */
              {
 -                sd->randomize_group[n] = TRUE;
 -                sd->boltzfac[n]        = BOLTZ*opts->ref_t[n];
 +                sd->randomize_group[gt] = TRUE;
 +                sd->boltzfac[gt]        = BOLTZ*opts->ref_t[gt];
              }
              else
              {
 -                sd->randomize_group[n] = FALSE;
 +                sd->randomize_group[gt] = FALSE;
              }
          }
      }
 +
      return sd;
  }
  
 -gmx_update_t init_update(t_inputrec *ir)
 +void update_temperature_constants(gmx_update_t *upd, const t_inputrec *ir)
 +{
 +    if (ir->eI == eiBD)
 +    {
 +        if (ir->bd_fric != 0)
 +        {
 +            for (int gt = 0; gt < ir->opts.ngtc; gt++)
 +            {
 +                upd->sd->bd_rf[gt] = std::sqrt(2.0*BOLTZ*ir->opts.ref_t[gt]/(ir->bd_fric*ir->delta_t));
 +            }
 +        }
 +        else
 +        {
 +            for (int gt = 0; gt < ir->opts.ngtc; gt++)
 +            {
 +                upd->sd->bd_rf[gt] = std::sqrt(2.0*BOLTZ*ir->opts.ref_t[gt]);
 +            }
 +        }
 +    }
 +    if (ir->eI == eiSD1)
 +    {
 +        for (int gt = 0; gt < ir->opts.ngtc; gt++)
 +        {
 +            real kT = BOLTZ*ir->opts.ref_t[gt];
 +            /* The mass is accounted for later, since this differs per atom */
 +            upd->sd->sdsig[gt].V  = std::sqrt(kT*(1 - upd->sd->sdc[gt].em*upd->sd->sdc[gt].em));
 +        }
 +    }
 +}
 +
 +gmx_update_t *init_update(const t_inputrec *ir)
  {
 -    t_gmx_update *upd;
 +    gmx_update_t *upd;
  
      snew(upd, 1);
  
          upd->sd    = init_stochd(ir);
      }
  
 +    update_temperature_constants(upd, ir);
 +
      upd->xp        = NULL;
      upd->xp_nalloc = 0;
  
      return upd;
  }
  
 +void update_realloc(gmx_update_t *upd, int state_nalloc)
 +{
 +    GMX_ASSERT(upd, "upd must be allocated before its fields can be reallocated");
 +    if (state_nalloc > upd->xp_nalloc)
 +    {
 +        upd->xp_nalloc = state_nalloc;
 +        /* We need to allocate one element extra, since we might use
 +         * (unaligned) 4-wide SIMD loads to access rvec entries. */
 +        srenew(upd->xp, upd->xp_nalloc + 1);
 +    }
 +}
 +
  static void do_update_sd1(gmx_stochd_t *sd,
                            int start, int nrend, double dt,
                            rvec accel[], ivec nFreeze[],
                            unsigned short cFREEZE[], unsigned short cACC[],
                            unsigned short cTC[],
                            rvec x[], rvec xprime[], rvec v[], rvec f[],
 -                          int ngtc, real ref_t[],
                            gmx_bool bDoConstr,
                            gmx_bool bFirstHalfConstr,
                            gmx_int64_t step, int seed, int* gatindex)
  {
      gmx_sd_const_t *sdc;
      gmx_sd_sigma_t *sig;
 -    real            kT;
      int             gf = 0, ga = 0, gt = 0;
      real            ism;
      int             n, d;
  
 +    // Even 0 bits internal counter gives 2x64 ints (more than enough for three table lookups)
 +    gmx::ThreeFry2x64<0> rng(seed, gmx::RandomDomain::UpdateCoordinates);
 +    gmx::TabulatedNormalDistribution<real, 14> dist;
 +
      sdc = sd->sdc;
      sig = sd->sdsig;
  
 -    for (n = 0; n < ngtc; n++)
 -    {
 -        kT = BOLTZ*ref_t[n];
 -        /* The mass is encounted for later, since this differs per atom */
 -        sig[n].V  = sqrt(kT*(1 - sdc[n].em*sdc[n].em));
 -    }
 -
      if (!bDoConstr)
      {
          for (n = start; n < nrend; n++)
          {
 -            real rnd[3];
              int  ng = gatindex ? gatindex[n] : n;
  
 -            ism = sqrt(invmass[n]);
 +            rng.restart(step, ng);
 +            dist.reset();
 +
 +            ism = std::sqrt(invmass[n]);
 +
              if (cFREEZE)
              {
                  gf  = cFREEZE[n];
                  gt  = cTC[n];
              }
  
 -            gmx_rng_cycle_3gaussian_table(step, ng, seed, RND_SEED_UPDATE, rnd);
 -
              for (d = 0; d < DIM; d++)
              {
                  if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
                  {
                      real sd_V, vn;
  
 -                    sd_V         = ism*sig[gt].V*rnd[d];
 +                    sd_V         = ism*sig[gt].V*dist(rng);
                      vn           = v[n][d] + (invmass[n]*f[n][d] + accel[ga][d])*dt;
                      v[n][d]      = vn*sdc[gt].em + sd_V;
                      /* Here we include half of the friction+noise
              /* Update friction and noise only */
              for (n = start; n < nrend; n++)
              {
 -                real rnd[3];
                  int  ng = gatindex ? gatindex[n] : n;
  
 -                ism = sqrt(invmass[n]);
 +                rng.restart(step, ng);
 +                dist.reset();
 +
 +                ism = std::sqrt(invmass[n]);
 +
                  if (cFREEZE)
                  {
                      gf  = cFREEZE[n];
                      gt  = cTC[n];
                  }
  
 -                gmx_rng_cycle_3gaussian_table(step, ng, seed, RND_SEED_UPDATE, rnd);
 -
                  for (d = 0; d < DIM; d++)
                  {
                      if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
                      {
                          real sd_V, vn;
  
 -                        sd_V         = ism*sig[gt].V*rnd[d];
 +                        sd_V         = ism*sig[gt].V*dist(rng);
                          vn           = v[n][d];
                          v[n][d]      = vn*sdc[gt].em + sd_V;
                          /* Add the friction and noise contribution only */
      }
  }
  
 -static void check_sd2_work_data_allocation(gmx_stochd_t *sd, int nrend)
 -{
 -    if (nrend > sd->sd_V_nalloc)
 -    {
 -        sd->sd_V_nalloc = over_alloc_dd(nrend);
 -        srenew(sd->sd_V, sd->sd_V_nalloc);
 -    }
 -}
 -
 -static void do_update_sd2_Tconsts(gmx_stochd_t *sd,
 -                                  int           ngtc,
 -                                  const real    tau_t[],
 -                                  const real    ref_t[])
 -{
 -    /* This is separated from the update below, because it is single threaded */
 -    gmx_sd_const_t *sdc;
 -    gmx_sd_sigma_t *sig;
 -    int             gt;
 -    real            kT;
 -
 -    sdc = sd->sdc;
 -    sig = sd->sdsig;
 -
 -    for (gt = 0; gt < ngtc; gt++)
 -    {
 -        kT = BOLTZ*ref_t[gt];
 -        /* The mass is encounted for later, since this differs per atom */
 -        sig[gt].V  = sqrt(kT*(1-sdc[gt].em));
 -        sig[gt].X  = sqrt(kT*sqr(tau_t[gt])*sdc[gt].c);
 -        sig[gt].Yv = sqrt(kT*sdc[gt].b/sdc[gt].c);
 -        sig[gt].Yx = sqrt(kT*sqr(tau_t[gt])*sdc[gt].b/(1-sdc[gt].em));
 -    }
 -}
 -
 -static void do_update_sd2(gmx_stochd_t *sd,
 -                          gmx_bool bInitStep,
 -                          int start, int nrend,
 -                          rvec accel[], ivec nFreeze[],
 -                          real invmass[], unsigned short ptype[],
 -                          unsigned short cFREEZE[], unsigned short cACC[],
 -                          unsigned short cTC[],
 -                          rvec x[], rvec xprime[], rvec v[], rvec f[],
 -                          rvec sd_X[],
 -                          const real tau_t[],
 -                          gmx_bool bFirstHalf, gmx_int64_t step, int seed,
 -                          int* gatindex)
 -{
 -    gmx_sd_const_t *sdc;
 -    gmx_sd_sigma_t *sig;
 -    /* The random part of the velocity update, generated in the first
 -     * half of the update, needs to be remembered for the second half.
 -     */
 -    rvec  *sd_V;
 -    int    gf = 0, ga = 0, gt = 0;
 -    real   vn = 0, Vmh, Xmh;
 -    real   ism;
 -    int    n, d, ng;
 -
 -    sdc  = sd->sdc;
 -    sig  = sd->sdsig;
 -    sd_V = sd->sd_V;
 -
 -    for (n = start; n < nrend; n++)
 -    {
 -        real rnd[6], rndi[3];
 -        ng  = gatindex ? gatindex[n] : n;
 -        ism = sqrt(invmass[n]);
 -        if (cFREEZE)
 -        {
 -            gf  = cFREEZE[n];
 -        }
 -        if (cACC)
 -        {
 -            ga  = cACC[n];
 -        }
 -        if (cTC)
 -        {
 -            gt  = cTC[n];
 -        }
 -
 -        gmx_rng_cycle_6gaussian_table(step*2+(bFirstHalf ? 1 : 2), ng, seed, RND_SEED_UPDATE, rnd);
 -        if (bInitStep)
 -        {
 -            gmx_rng_cycle_3gaussian_table(step*2, ng, seed, RND_SEED_UPDATE, rndi);
 -        }
 -        for (d = 0; d < DIM; d++)
 -        {
 -            if (bFirstHalf)
 -            {
 -                vn             = v[n][d];
 -            }
 -            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 -            {
 -                if (bFirstHalf)
 -                {
 -                    if (bInitStep)
 -                    {
 -                        sd_X[n][d] = ism*sig[gt].X*rndi[d];
 -                    }
 -                    Vmh = sd_X[n][d]*sdc[gt].d/(tau_t[gt]*sdc[gt].c)
 -                        + ism*sig[gt].Yv*rnd[d*2];
 -                    sd_V[n][d] = ism*sig[gt].V*rnd[d*2+1];
 -
 -                    v[n][d] = vn*sdc[gt].em
 -                        + (invmass[n]*f[n][d] + accel[ga][d])*tau_t[gt]*(1 - sdc[gt].em)
 -                        + sd_V[n][d] - sdc[gt].em*Vmh;
 -
 -                    xprime[n][d] = x[n][d] + v[n][d]*tau_t[gt]*(sdc[gt].eph - sdc[gt].emh);
 -                }
 -                else
 -                {
 -                    /* Correct the velocities for the constraints.
 -                     * This operation introduces some inaccuracy,
 -                     * since the velocity is determined from differences in coordinates.
 -                     */
 -                    v[n][d] =
 -                        (xprime[n][d] - x[n][d])/(tau_t[gt]*(sdc[gt].eph - sdc[gt].emh));
 -
 -                    Xmh = sd_V[n][d]*tau_t[gt]*sdc[gt].d/(sdc[gt].em-1)
 -                        + ism*sig[gt].Yx*rnd[d*2];
 -                    sd_X[n][d] = ism*sig[gt].X*rnd[d*2+1];
 -
 -                    xprime[n][d] += sd_X[n][d] - Xmh;
 -
 -                }
 -            }
 -            else
 -            {
 -                if (bFirstHalf)
 -                {
 -                    v[n][d]        = 0.0;
 -                    xprime[n][d]   = x[n][d];
 -                }
 -            }
 -        }
 -    }
 -}
 -
 -static void do_update_bd_Tconsts(double dt, real friction_coefficient,
 -                                 int ngtc, const real ref_t[],
 -                                 real *rf)
 -{
 -    /* This is separated from the update below, because it is single threaded */
 -    int gt;
 -
 -    if (friction_coefficient != 0)
 -    {
 -        for (gt = 0; gt < ngtc; gt++)
 -        {
 -            rf[gt] = sqrt(2.0*BOLTZ*ref_t[gt]/(friction_coefficient*dt));
 -        }
 -    }
 -    else
 -    {
 -        for (gt = 0; gt < ngtc; gt++)
 -        {
 -            rf[gt] = sqrt(2.0*BOLTZ*ref_t[gt]);
 -        }
 -    }
 -}
 -
  static void do_update_bd(int start, int nrend, double dt,
                           ivec nFreeze[],
                           real invmass[], unsigned short ptype[],
      real   vn;
      real   invfr = 0;
      int    n, d;
 +    // Use 1 bit of internal counters to give us 2*2 64-bits values per stream
 +    // Each 64-bit value is enough for 4 normal distribution table numbers.
 +    gmx::ThreeFry2x64<0> rng(seed, gmx::RandomDomain::UpdateCoordinates);
 +    gmx::TabulatedNormalDistribution<real, 14> dist;
  
      if (friction_coefficient != 0)
      {
  
      for (n = start; (n < nrend); n++)
      {
 -        real rnd[3];
          int  ng  = gatindex ? gatindex[n] : n;
  
 +        rng.restart(step, ng);
 +        dist.reset();
 +
          if (cFREEZE)
          {
              gf = cFREEZE[n];
          {
              gt = cTC[n];
          }
 -        gmx_rng_cycle_3gaussian_table(step, ng, seed, RND_SEED_UPDATE, rnd);
          for (d = 0; (d < DIM); d++)
          {
              if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
              {
                  if (friction_coefficient != 0)
                  {
 -                    vn = invfr*f[n][d] + rf[gt]*rnd[d];
 +                    vn = invfr*f[n][d] + rf[gt]*dist(rng);
                  }
                  else
                  {
                      /* NOTE: invmass = 2/(mass*friction_constant*dt) */
                      vn = 0.5*invmass[n]*f[n][d]*dt
 -                        + sqrt(0.5*invmass[n])*rf[gt]*rnd[d];
 +                        + std::sqrt(0.5*invmass[n])*rf[gt]*dist(rng);
                  }
  
                  v[n][d]      = vn;
@@@ -840,9 -989,6 +840,9 @@@ static void calc_ke_part_normal(rvec v[
  #pragma omp parallel for num_threads(nthread) schedule(static)
      for (thread = 0; thread < nthread; thread++)
      {
 +        // This OpenMP only loops over arrays and does not call any functions
 +        // or memory allocation. It should not be able to throw, so for now
 +        // we do not need a try/catch wrapper.
          int     start_t, end_t, n;
          int     ga, gt;
          rvec    v_corrt;
@@@ -1040,7 -1186,7 +1040,7 @@@ void update_ekinstate(ekinstate_t *ekin
  }
  
  void restore_ekinstate_from_state(t_commrec *cr,
 -                                  gmx_ekindata_t *ekind, ekinstate_t *ekinstate)
 +                                  gmx_ekindata_t *ekind, const ekinstate_t *ekinstate)
  {
      int i, n;
  
      }
  }
  
 -void set_deform_reference_box(gmx_update_t upd, gmx_int64_t step, matrix box)
 +void set_deform_reference_box(gmx_update_t *upd, gmx_int64_t step, matrix box)
  {
      upd->deformref_step = step;
      copy_mat(box, upd->deformref_box);
  }
  
 -static void deform(gmx_update_t upd,
 +static void deform(gmx_update_t *upd,
                     int start, int homenr, rvec x[], matrix box,
                     const t_inputrec *ir, gmx_int64_t step)
  {
              }
          }
      }
 -    m_inv_ur0(box, invbox);
 +    gmx::invertBoxMatrix(box, invbox);
      copy_mat(bnew, box);
      mmul_ur0(box, invbox, mu);
  
@@@ -1161,7 -1307,7 +1161,7 @@@ void update_tcouple(gmx_int64_t       s
  
      /* if using vv with trotter decomposition methods, we do this elsewhere in the code */
      if (inputrec->etc != etcNO &&
 -        !(IR_NVT_TROTTER(inputrec) || IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec)))
 +        !(inputrecNvtTrotter(inputrec) || inputrecNptTrotter(inputrec) || inputrecNphTrotter(inputrec)))
      {
          /* We should only couple after a step where energies were determined (for leapfrog versions)
             or the step energies are determined, for velocity verlet versions */
@@@ -1228,7 -1374,7 +1228,7 @@@ void update_pcouple(FILE             *f
      int        i;
  
      /* if using Trotter pressure, we do this in coupling.c, so we leave it false. */
 -    if (inputrec->epc != epcNO && (!(IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec))))
 +    if (inputrec->epc != epcNO && (!(inputrecNptTrotter(inputrec) || inputrecNphTrotter(inputrec))))
      {
          /* We should only couple after a step where energies were determined */
          bPCouple = (inputrec->nstpcouple == 1 ||
      }
  }
  
 -static rvec *get_xprime(const t_state *state, gmx_update_t upd)
 -{
 -    if (state->nalloc > upd->xp_nalloc)
 -    {
 -        upd->xp_nalloc = state->nalloc;
 -        srenew(upd->xp, upd->xp_nalloc);
 -    }
 -
 -    return upd->xp;
 -}
 -
 -static void combine_forces(gmx_update_t upd,
 -                           int nstcalclr,
 -                           gmx_constr_t constr,
 -                           t_inputrec *ir, t_mdatoms *md, t_idef *idef,
 -                           t_commrec *cr,
 -                           gmx_int64_t step,
 -                           t_state *state, gmx_bool bMolPBC,
 -                           int start, int nrend,
 -                           rvec f[], rvec f_lr[],
 -                           tensor *vir_lr_constr,
 -                           t_nrnb *nrnb)
 -{
 -    int  i, d;
 -
 -    /* f contains the short-range forces + the long range forces
 -     * which are stored separately in f_lr.
 -     */
 -
 -    if (constr != NULL && vir_lr_constr != NULL &&
 -        !(ir->eConstrAlg == econtSHAKE && ir->epc == epcNO))
 -    {
 -        /* We need to constrain the LR forces separately,
 -         * because due to the different pre-factor for the SR and LR
 -         * forces in the update algorithm, we have to correct
 -         * the constraint virial for the nstcalclr-1 extra f_lr.
 -         * Constrain only the additional LR part of the force.
 -         */
 -        /* MRS -- need to make sure this works with trotter integration -- the constraint calls may not be right.*/
 -        rvec *xp;
 -        real  fac;
 -        int   gf = 0;
 -
 -        xp  = get_xprime(state, upd);
 -
 -        fac = (nstcalclr - 1)*ir->delta_t*ir->delta_t;
 -
 -        for (i = 0; i < md->homenr; i++)
 -        {
 -            if (md->cFREEZE != NULL)
 -            {
 -                gf = md->cFREEZE[i];
 -            }
 -            for (d = 0; d < DIM; d++)
 -            {
 -                if ((md->ptype[i] != eptVSite) &&
 -                    (md->ptype[i] != eptShell) &&
 -                    !ir->opts.nFreeze[gf][d])
 -                {
 -                    xp[i][d] = state->x[i][d] + fac*f_lr[i][d]*md->invmass[i];
 -                }
 -                else
 -                {
 -                    xp[i][d] = state->x[i][d];
 -                }
 -            }
 -        }
 -        constrain(NULL, FALSE, FALSE, constr, idef, ir, cr, step, 0, 1.0, md,
 -                  state->x, xp, xp, bMolPBC, state->box, state->lambda[efptBONDED], NULL,
 -                  NULL, vir_lr_constr, nrnb, econqForce);
 -    }
 -
 -    /* Add nstcalclr-1 times the LR force to the sum of both forces
 -     * and store the result in forces_lr.
 -     */
 -    for (i = start; i < nrend; i++)
 -    {
 -        for (d = 0; d < DIM; d++)
 -        {
 -            f_lr[i][d] = f[i][d] + (nstcalclr - 1)*f_lr[i][d];
 -        }
 -    }
 -}
 -
  void update_constraints(FILE             *fplog,
                          gmx_int64_t       step,
                          real             *dvdlambda, /* the contribution to be added to the bonded interactions */
                          t_commrec        *cr,
                          t_nrnb           *nrnb,
                          gmx_wallcycle_t   wcycle,
 -                        gmx_update_t      upd,
 +                        gmx_update_t     *upd,
                          gmx_constr_t      constr,
                          gmx_bool          bFirstHalf,
                          gmx_bool          bCalcVir)
  {
      gmx_bool             bLastStep, bLog = FALSE, bEner = FALSE, bDoConstr = FALSE;
      double               dt;
 -    int                  start, homenr, nrend, i, m;
 +    int                  start, homenr, nrend, i;
      tensor               vir_con;
 -    rvec                *xprime = NULL;
      int                  nth, th;
  
      if (constr)
          /* clear out constraints before applying */
          clear_mat(vir_part);
  
 -        xprime = get_xprime(state, upd);
 -
          bLastStep = (step == inputrec->init_step+inputrec->nsteps);
          bLog      = (do_per_step(step, inputrec->nstlog) || bLastStep || (step < 0));
          bEner     = (do_per_step(step, inputrec->nstenergy) || bLastStep);
 -        /* Constrain the coordinates xprime */
 +        /* Constrain the coordinates upd->xp */
          wallcycle_start(wcycle, ewcCONSTR);
          if (EI_VV(inputrec->eI) && bFirstHalf)
          {
          {
              constrain(NULL, bLog, bEner, constr, idef,
                        inputrec, cr, step, 1, 1.0, md,
 -                      state->x, xprime, NULL,
 +                      state->x, upd->xp, NULL,
                        bMolPBC, state->box,
                        state->lambda[efptBONDED], dvdlambda,
                        state->v, bCalcVir ? &vir_con : NULL, nrnb, econqCoord);
          where();
  
          dump_it_all(fplog, "After Shake",
 -                    state->natoms, state->x, xprime, state->v, force);
 +                    state->natoms, state->x, upd->xp, state->v, force);
  
          if (bCalcVir)
          {
 -            if (inputrec->eI == eiSD2)
 -            {
 -                /* A correction factor eph is needed for the SD constraint force */
 -                /* Here we can, unfortunately, not have proper corrections
 -                 * for different friction constants, so we use the first one.
 -                 */
 -                for (i = 0; i < DIM; i++)
 -                {
 -                    for (m = 0; m < DIM; m++)
 -                    {
 -                        vir_part[i][m] += upd->sd->sdc[0].eph*vir_con[i][m];
 -                    }
 -                }
 -            }
 -            else
 -            {
 -                m_add(vir_part, vir_con, vir_part);
 -            }
 +            m_add(vir_part, vir_con, vir_part);
              if (debug)
              {
                  pr_rvecs(debug, 0, "constraint virial", vir_part, DIM);
      if (inputrec->eI == eiSD1 && bDoConstr && !bFirstHalf)
      {
          wallcycle_start(wcycle, ewcUPDATE);
 -        xprime = get_xprime(state, upd);
  
          nth = gmx_omp_nthreads_get(emntUpdate);
  
  #pragma omp parallel for num_threads(nth) schedule(static)
 -
          for (th = 0; th < nth; th++)
          {
 -            int start_th, end_th;
 +            try
 +            {
 +                int start_th, end_th;
  
 -            start_th = start + ((nrend-start)* th   )/nth;
 -            end_th   = start + ((nrend-start)*(th+1))/nth;
 +                start_th = start + ((nrend-start)* th   )/nth;
 +                end_th   = start + ((nrend-start)*(th+1))/nth;
  
 -            /* The second part of the SD integration */
 -            do_update_sd1(upd->sd,
 -                          start_th, end_th, dt,
 -                          inputrec->opts.acc, inputrec->opts.nFreeze,
 -                          md->invmass, md->ptype,
 -                          md->cFREEZE, md->cACC, md->cTC,
 -                          state->x, xprime, state->v, force,
 -                          inputrec->opts.ngtc, inputrec->opts.ref_t,
 -                          bDoConstr, FALSE,
 -                          step, inputrec->ld_seed,
 -                          DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                /* The second part of the SD integration */
 +                do_update_sd1(upd->sd,
 +                              start_th, end_th, dt,
 +                              inputrec->opts.acc, inputrec->opts.nFreeze,
 +                              md->invmass, md->ptype,
 +                              md->cFREEZE, md->cACC, md->cTC,
 +                              state->x, upd->xp, state->v, force,
 +                              bDoConstr, FALSE,
 +                              step, inputrec->ld_seed,
 +                              DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +            }
 +            GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
          }
          inc_nrnb(nrnb, eNR_UPDATE, homenr);
          wallcycle_stop(wcycle, ewcUPDATE);
  
          if (bDoConstr)
          {
 -            /* Constrain the coordinates xprime for half a time step */
 +            /* Constrain the coordinates upd->xp for half a time step */
              wallcycle_start(wcycle, ewcCONSTR);
  
              constrain(NULL, bLog, bEner, constr, idef,
                        inputrec, cr, step, 1, 0.5, md,
 -                      state->x, xprime, NULL,
 +                      state->x, upd->xp, NULL,
                        bMolPBC, state->box,
                        state->lambda[efptBONDED], dvdlambda,
                        state->v, NULL, nrnb, econqCoord);
          }
      }
  
 -    if ((inputrec->eI == eiSD2) && !(bFirstHalf))
 -    {
 -        wallcycle_start(wcycle, ewcUPDATE);
 -        xprime = get_xprime(state, upd);
 -
 -        nth = gmx_omp_nthreads_get(emntUpdate);
 -
 -#pragma omp parallel for num_threads(nth) schedule(static)
 -        for (th = 0; th < nth; th++)
 -        {
 -            int start_th, end_th;
 -
 -            start_th = start + ((nrend-start)* th   )/nth;
 -            end_th   = start + ((nrend-start)*(th+1))/nth;
 -
 -            /* The second part of the SD integration */
 -            do_update_sd2(upd->sd,
 -                          FALSE, start_th, end_th,
 -                          inputrec->opts.acc, inputrec->opts.nFreeze,
 -                          md->invmass, md->ptype,
 -                          md->cFREEZE, md->cACC, md->cTC,
 -                          state->x, xprime, state->v, force, state->sd_X,
 -                          inputrec->opts.tau_t,
 -                          FALSE, step, inputrec->ld_seed,
 -                          DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 -        }
 -        inc_nrnb(nrnb, eNR_UPDATE, homenr);
 -        wallcycle_stop(wcycle, ewcUPDATE);
 -
 -        if (bDoConstr)
 -        {
 -            /* Constrain the coordinates xprime */
 -            wallcycle_start(wcycle, ewcCONSTR);
 -            constrain(NULL, bLog, bEner, constr, idef,
 -                      inputrec, cr, step, 1, 1.0, md,
 -                      state->x, xprime, NULL,
 -                      bMolPBC, state->box,
 -                      state->lambda[efptBONDED], dvdlambda,
 -                      NULL, NULL, nrnb, econqCoord);
 -            wallcycle_stop(wcycle, ewcCONSTR);
 -        }
 -    }
 -
 -
      /* We must always unshift after updating coordinates; if we did not shake
         x was shifted in do_force */
  
           */
          wallcycle_start_nocount(wcycle, ewcUPDATE);
  
+         if (md->cFREEZE != NULL && constr != NULL)
+         {
+             /* If we have atoms that are frozen along some, but not all
+              * dimensions, the constraints will have moved them also along
+              * the frozen dimensions. To freeze such degrees of freedom
+              * we copy them back here to later copy them forward. It would
+              * be more elegant and slightly more efficient to copies zero
+              * times instead of twice, but the graph case below prevents this.
+              */
+             const ivec *nFreeze                     = inputrec->opts.nFreeze;
+             bool        partialFreezeAndConstraints = false;
+             for (int g = 0; g < inputrec->opts.ngfrz; g++)
+             {
+                 int numFreezeDim = nFreeze[g][XX] + nFreeze[g][YY] + nFreeze[g][ZZ];
+                 if (numFreezeDim > 0 && numFreezeDim < 3)
+                 {
+                     partialFreezeAndConstraints = true;
+                 }
+             }
+             if (partialFreezeAndConstraints)
+             {
+                 for (int i = start; i < nrend; i++)
+                 {
+                     int g = md->cFREEZE[i];
+                     for (int d = 0; d < DIM; d++)
+                     {
+                         if (nFreeze[g][d])
+                         {
+                             upd->xp[i][d] = state->x[i][d];
+                         }
+                     }
+                 }
+             }
+         }
          if (graph && (graph->nnodes > 0))
          {
              unshift_x(graph, state->box, state->x, upd->xp);
  #pragma omp parallel for num_threads(nth) schedule(static)
              for (i = start; i < nrend; i++)
              {
 +                // Trivial statement, does not throw
                  copy_rvec(upd->xp[i], state->x[i]);
              }
          }
@@@ -1476,7 -1804,7 +1512,7 @@@ void update_box(FILE             *fplog
                  rvec              force[],   /* forces on home particles */
                  matrix            pcoupl_mu,
                  t_nrnb           *nrnb,
 -                gmx_update_t      upd)
 +                gmx_update_t     *upd)
  {
      double               dt;
      int                  start, homenr, i, n, m;
              break;
      }
  
 -    if (DEFORM(*inputrec))
 +    if (inputrecDeform(inputrec))
      {
          deform(upd, start, homenr, state->x, state->box, inputrec, step);
      }
@@@ -1568,18 -1896,27 +1604,18 @@@ void update_coords(FILE             *fp
                     t_inputrec       *inputrec,  /* input record and box stuff */
                     t_mdatoms        *md,
                     t_state          *state,
 -                   gmx_bool          bMolPBC,
                     rvec             *f,    /* forces on home particles */
 -                   gmx_bool          bDoLR,
 -                   rvec             *f_lr,
 -                   tensor           *vir_lr_constr,
                     t_fcdata         *fcd,
                     gmx_ekindata_t   *ekind,
                     matrix            M,
 -                   gmx_update_t      upd,
 -                   gmx_bool          bInitStep,
 +                   gmx_update_t     *upd,
                     int               UpdatePart,
                     t_commrec        *cr, /* these shouldn't be here -- need to think about it */
 -                   t_nrnb           *nrnb,
 -                   gmx_constr_t      constr,
 -                   t_idef           *idef)
 +                   gmx_constr_t      constr)
  {
      gmx_bool          bNH, bPR, bDoConstr = FALSE;
      double            dt, alpha;
 -    rvec             *force;
      int               start, homenr, nrend;
 -    rvec             *xprime;
      int               nth, th;
  
      bDoConstr = (NULL != constr);
      homenr = md->homenr;
      nrend  = start+homenr;
  
 -    xprime = get_xprime(state, upd);
 -
      dt   = inputrec->delta_t;
  
      /* We need to update the NMR restraint history when time averaging is used */
      bNH = inputrec->etc == etcNOSEHOOVER;
      bPR = ((inputrec->epc == epcPARRINELLORAHMAN) || (inputrec->epc == epcMTTK));
  
 -    if (bDoLR && inputrec->nstcalclr > 1 && !EI_VV(inputrec->eI))  /* get this working with VV? */
 -    {
 -        /* Store the total force + nstcalclr-1 times the LR force
 -         * in forces_lr, so it can be used in a normal update algorithm
 -         * to produce twin time stepping.
 -         */
 -        /* is this correct in the new construction? MRS */
 -        combine_forces(upd,
 -                       inputrec->nstcalclr, constr, inputrec, md, idef, cr,
 -                       step, state, bMolPBC,
 -                       start, nrend, f, f_lr, vir_lr_constr, nrnb);
 -        force = f_lr;
 -    }
 -    else
 -    {
 -        force = f;
 -    }
 -
      /* ############# START The update of velocities and positions ######### */
      where();
      dump_it_all(fplog, "Before update",
 -                state->natoms, state->x, xprime, state->v, force);
 -
 -    if (inputrec->eI == eiSD2)
 -    {
 -        check_sd2_work_data_allocation(upd->sd, nrend);
 -
 -        do_update_sd2_Tconsts(upd->sd,
 -                              inputrec->opts.ngtc,
 -                              inputrec->opts.tau_t,
 -                              inputrec->opts.ref_t);
 -    }
 -    if (inputrec->eI == eiBD)
 -    {
 -        do_update_bd_Tconsts(dt, inputrec->bd_fric,
 -                             inputrec->opts.ngtc, inputrec->opts.ref_t,
 -                             upd->sd->bd_rf);
 -    }
 +                state->natoms, state->x, upd->xp, state->v, f);
  
      nth = gmx_omp_nthreads_get(emntUpdate);
  
  #pragma omp parallel for num_threads(nth) schedule(static) private(alpha)
      for (th = 0; th < nth; th++)
      {
 -        int start_th, end_th;
 +        try
 +        {
 +            int start_th, end_th;
  
 -        start_th = start + ((nrend-start)* th   )/nth;
 -        end_th   = start + ((nrend-start)*(th+1))/nth;
 +            start_th = start + ((nrend-start)* th   )/nth;
 +            end_th   = start + ((nrend-start)*(th+1))/nth;
  
 -        switch (inputrec->eI)
 -        {
 -            case (eiMD):
 -                if (ekind->cosacc.cos_accel == 0)
 -                {
 -                    do_update_md(start_th, end_th, dt,
 -                                 ekind->tcstat, state->nosehoover_vxi,
 -                                 ekind->bNEMD, ekind->grpstat, inputrec->opts.acc,
 -                                 inputrec->opts.nFreeze,
 -                                 md->invmass, md->ptype,
 -                                 md->cFREEZE, md->cACC, md->cTC,
 -                                 state->x, xprime, state->v, force, M,
 -                                 bNH, bPR);
 -                }
 -                else
 -                {
 -                    do_update_visc(start_th, end_th, dt,
 -                                   ekind->tcstat, state->nosehoover_vxi,
 -                                   md->invmass, md->ptype,
 -                                   md->cTC, state->x, xprime, state->v, force, M,
 -                                   state->box,
 -                                   ekind->cosacc.cos_accel,
 -                                   ekind->cosacc.vcos,
 -                                   bNH, bPR);
 -                }
 -                break;
 -            case (eiSD1):
 -                /* With constraints, the SD1 update is done in 2 parts */
 -                do_update_sd1(upd->sd,
 -                              start_th, end_th, dt,
 -                              inputrec->opts.acc, inputrec->opts.nFreeze,
 -                              md->invmass, md->ptype,
 -                              md->cFREEZE, md->cACC, md->cTC,
 -                              state->x, xprime, state->v, force,
 -                              inputrec->opts.ngtc, inputrec->opts.ref_t,
 -                              bDoConstr, TRUE,
 -                              step, inputrec->ld_seed, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 -                break;
 -            case (eiSD2):
 -                /* The SD2 update is always done in 2 parts,
 -                 * because an extra constraint step is needed
 -                 */
 -                do_update_sd2(upd->sd,
 -                              bInitStep, start_th, end_th,
 -                              inputrec->opts.acc, inputrec->opts.nFreeze,
 -                              md->invmass, md->ptype,
 -                              md->cFREEZE, md->cACC, md->cTC,
 -                              state->x, xprime, state->v, force, state->sd_X,
 -                              inputrec->opts.tau_t,
 -                              TRUE, step, inputrec->ld_seed,
 -                              DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 -                break;
 -            case (eiBD):
 -                do_update_bd(start_th, end_th, dt,
 -                             inputrec->opts.nFreeze, md->invmass, md->ptype,
 -                             md->cFREEZE, md->cTC,
 -                             state->x, xprime, state->v, force,
 -                             inputrec->bd_fric,
 -                             upd->sd->bd_rf,
 -                             step, inputrec->ld_seed, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 -                break;
 -            case (eiVV):
 -            case (eiVVAK):
 -                alpha = 1.0 + DIM/((double)inputrec->opts.nrdf[0]); /* assuming barostat coupled to group 0. */
 -                switch (UpdatePart)
 -                {
 -                    case etrtVELOCITY1:
 -                    case etrtVELOCITY2:
 -                        do_update_vv_vel(start_th, end_th, dt,
 -                                         inputrec->opts.acc, inputrec->opts.nFreeze,
 -                                         md->invmass, md->ptype,
 -                                         md->cFREEZE, md->cACC,
 -                                         state->v, force,
 -                                         (bNH || bPR), state->veta, alpha);
 -                        break;
 -                    case etrtPOSITION:
 -                        do_update_vv_pos(start_th, end_th, dt,
 -                                         inputrec->opts.nFreeze,
 -                                         md->ptype, md->cFREEZE,
 -                                         state->x, xprime, state->v,
 -                                         (bNH || bPR), state->veta);
 -                        break;
 -                }
 -                break;
 -            default:
 -                gmx_fatal(FARGS, "Don't know how to update coordinates");
 -                break;
 +            switch (inputrec->eI)
 +            {
 +                case (eiMD):
 +                    if (ekind->cosacc.cos_accel == 0)
 +                    {
 +                        do_update_md(start_th, end_th, dt,
 +                                     ekind->tcstat, state->nosehoover_vxi,
 +                                     ekind->bNEMD, ekind->grpstat, inputrec->opts.acc,
 +                                     inputrec->opts.nFreeze,
 +                                     md->invmass, md->ptype,
 +                                     md->cFREEZE, md->cACC, md->cTC,
 +                                     state->x, upd->xp, state->v, f, M,
 +                                     bNH, bPR);
 +                    }
 +                    else
 +                    {
 +                        do_update_visc(start_th, end_th, dt,
 +                                       ekind->tcstat, state->nosehoover_vxi,
 +                                       md->invmass, md->ptype,
 +                                       md->cTC, state->x, upd->xp, state->v, f, M,
 +                                       state->box,
 +                                       ekind->cosacc.cos_accel,
 +                                       ekind->cosacc.vcos,
 +                                       bNH, bPR);
 +                    }
 +                    break;
 +                case (eiSD1):
 +                    /* With constraints, the SD1 update is done in 2 parts */
 +                    do_update_sd1(upd->sd,
 +                                  start_th, end_th, dt,
 +                                  inputrec->opts.acc, inputrec->opts.nFreeze,
 +                                  md->invmass, md->ptype,
 +                                  md->cFREEZE, md->cACC, md->cTC,
 +                                  state->x, upd->xp, state->v, f,
 +                                  bDoConstr, TRUE,
 +                                  step, inputrec->ld_seed, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                    break;
 +                case (eiBD):
 +                    do_update_bd(start_th, end_th, dt,
 +                                 inputrec->opts.nFreeze, md->invmass, md->ptype,
 +                                 md->cFREEZE, md->cTC,
 +                                 state->x, upd->xp, state->v, f,
 +                                 inputrec->bd_fric,
 +                                 upd->sd->bd_rf,
 +                                 step, inputrec->ld_seed, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                    break;
 +                case (eiVV):
 +                case (eiVVAK):
 +                    alpha = 1.0 + DIM/((double)inputrec->opts.nrdf[0]); /* assuming barostat coupled to group 0. */
 +                    switch (UpdatePart)
 +                    {
 +                        case etrtVELOCITY1:
 +                        case etrtVELOCITY2:
 +                            do_update_vv_vel(start_th, end_th, dt,
 +                                             inputrec->opts.acc, inputrec->opts.nFreeze,
 +                                             md->invmass, md->ptype,
 +                                             md->cFREEZE, md->cACC,
 +                                             state->v, f,
 +                                             (bNH || bPR), state->veta, alpha);
 +                            break;
 +                        case etrtPOSITION:
 +                            do_update_vv_pos(start_th, end_th, dt,
 +                                             inputrec->opts.nFreeze,
 +                                             md->ptype, md->cFREEZE,
 +                                             state->x, upd->xp, state->v,
 +                                             (bNH || bPR), state->veta);
 +                            break;
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Don't know how to update coordinates");
 +                    break;
 +            }
          }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
      }
  
  }
@@@ -1763,7 -2147,7 +1799,7 @@@ void correct_ekin(FILE *log, int start
  }
  
  extern gmx_bool update_randomize_velocities(t_inputrec *ir, gmx_int64_t step, const t_commrec *cr,
 -                                            t_mdatoms *md, t_state *state, gmx_update_t upd, gmx_constr_t constr)
 +                                            t_mdatoms *md, t_state *state, gmx_update_t *upd, gmx_constr_t constr)
  {
  
      real rate = (ir->delta_t)/ir->opts.tau_t[0];
index fa6a1987497a93e02dbf72f689bc3a30f8d87cd3,0000000000000000000000000000000000000000..2b52c178a32c6f43df8d33d4a246ef9fd0900c7e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1611 -1,0 +1,1614 @@@
-                     ssd += fabs(2*(f - numf)/(f + numf));
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "forcetable.h"
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fileio/xvgr.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/units.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdtypes/fcdata.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/nblist.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +/* All the possible (implemented) table functions */
 +enum {
 +    etabLJ6,
 +    etabLJ12,
 +    etabLJ6Shift,
 +    etabLJ12Shift,
 +    etabShift,
 +    etabRF,
 +    etabRF_ZERO,
 +    etabCOUL,
 +    etabEwald,
 +    etabEwaldSwitch,
 +    etabEwaldUser,
 +    etabEwaldUserSwitch,
 +    etabLJ6Ewald,
 +    etabLJ6Switch,
 +    etabLJ12Switch,
 +    etabCOULSwitch,
 +    etabLJ6Encad,
 +    etabLJ12Encad,
 +    etabCOULEncad,
 +    etabEXPMIN,
 +    etabUSER,
 +    etabNR
 +};
 +
 +/** Evaluates to true if the table type contains user data. */
 +#define ETAB_USER(e)  ((e) == etabUSER || \
 +                       (e) == etabEwaldUser || (e) == etabEwaldUserSwitch)
 +
 +typedef struct {
 +    const char *name;
 +    gmx_bool    bCoulomb;
 +} t_tab_props;
 +
 +/* This structure holds name and a flag that tells whether
 +   this is a Coulomb type funtion */
 +static const t_tab_props tprops[etabNR] = {
 +    { "LJ6",  FALSE },
 +    { "LJ12", FALSE },
 +    { "LJ6Shift", FALSE },
 +    { "LJ12Shift", FALSE },
 +    { "Shift", TRUE },
 +    { "RF", TRUE },
 +    { "RF-zero", TRUE },
 +    { "COUL", TRUE },
 +    { "Ewald", TRUE },
 +    { "Ewald-Switch", TRUE },
 +    { "Ewald-User", TRUE },
 +    { "Ewald-User-Switch", TRUE },
 +    { "LJ6Ewald", FALSE },
 +    { "LJ6Switch", FALSE },
 +    { "LJ12Switch", FALSE },
 +    { "COULSwitch", TRUE },
 +    { "LJ6-Encad shift", FALSE },
 +    { "LJ12-Encad shift", FALSE },
 +    { "COUL-Encad shift",  TRUE },
 +    { "EXPMIN", FALSE },
 +    { "USER", FALSE },
 +};
 +
 +typedef struct {
 +    int     nx, nx0;
 +    double  tabscale;
 +    double *x, *v, *f;
 +} t_tabledata;
 +
 +double v_q_ewald_lr(double beta, double r)
 +{
 +    if (r == 0)
 +    {
 +        return beta*2/sqrt(M_PI);
 +    }
 +    else
 +    {
 +        return std::erf(beta*r)/r;
 +    }
 +}
 +
 +double v_lj_ewald_lr(double beta, double r)
 +{
 +    double br, br2, br4, r6, factor;
 +    if (r == 0)
 +    {
 +        return gmx::power6(beta)/6;
 +    }
 +    else
 +    {
 +        br     = beta*r;
 +        br2    = br*br;
 +        br4    = br2*br2;
 +        r6     = gmx::power6(r);
 +        factor = (1.0 - std::exp(-br2)*(1 + br2 + 0.5*br4))/r6;
 +        return factor;
 +    }
 +}
 +
 +void table_spline3_fill_ewald_lr(real                                 *table_f,
 +                                 real                                 *table_v,
 +                                 real                                 *table_fdv0,
 +                                 int                                   ntab,
 +                                 double                                dx,
 +                                 real                                  beta,
 +                                 real_space_grid_contribution_computer v_lr)
 +{
 +    real     tab_max;
 +    int      i, i_inrange;
 +    double   dc, dc_new;
 +    gmx_bool bOutOfRange;
 +    double   v_r0, v_r1, v_inrange, vi, a0, a1, a2dx;
 +    double   x_r0;
 +
 +    /* This function is called using either v_ewald_lr or v_lj_ewald_lr as a function argument
 +     * depending on wether we should create electrostatic or Lennard-Jones Ewald tables.
 +     */
 +
 +    if (ntab < 2)
 +    {
 +        gmx_fatal(FARGS, "Can not make a spline table with less than 2 points");
 +    }
 +
 +    /* We need some margin to be able to divide table values by r
 +     * in the kernel and also to do the integration arithmetics
 +     * without going out of range. Furthemore, we divide by dx below.
 +     */
 +    tab_max = GMX_REAL_MAX*0.0001;
 +
 +    /* This function produces a table with:
 +     * maximum energy error: V'''/(6*12*sqrt(3))*dx^3
 +     * maximum force error:  V'''/(6*4)*dx^2
 +     * The rms force error is the max error times 1/sqrt(5)=0.45.
 +     */
 +
 +    bOutOfRange = FALSE;
 +    i_inrange   = ntab;
 +    v_inrange   = 0;
 +    dc          = 0;
 +    for (i = ntab-1; i >= 0; i--)
 +    {
 +        x_r0 = i*dx;
 +
 +        v_r0 = (*v_lr)(beta, x_r0);
 +
 +        if (!bOutOfRange)
 +        {
 +            i_inrange = i;
 +            v_inrange = v_r0;
 +
 +            vi = v_r0;
 +        }
 +        else
 +        {
 +            /* Linear continuation for the last point in range */
 +            vi = v_inrange - dc*(i - i_inrange)*dx;
 +        }
 +
 +        if (table_v != NULL)
 +        {
 +            table_v[i] = vi;
 +        }
 +
 +        if (i == 0)
 +        {
 +            continue;
 +        }
 +
 +        /* Get the potential at table point i-1 */
 +        v_r1 = (*v_lr)(beta, (i-1)*dx);
 +
 +        if (v_r1 != v_r1 || v_r1 < -tab_max || v_r1 > tab_max)
 +        {
 +            bOutOfRange = TRUE;
 +        }
 +
 +        if (!bOutOfRange)
 +        {
 +            /* Calculate the average second derivative times dx over interval i-1 to i.
 +             * Using the function values at the end points and in the middle.
 +             */
 +            a2dx = (v_r0 + v_r1 - 2*(*v_lr)(beta, x_r0-0.5*dx))/(0.25*dx);
 +            /* Set the derivative of the spline to match the difference in potential
 +             * over the interval plus the average effect of the quadratic term.
 +             * This is the essential step for minimizing the error in the force.
 +             */
 +            dc = (v_r0 - v_r1)/dx + 0.5*a2dx;
 +        }
 +
 +        if (i == ntab - 1)
 +        {
 +            /* Fill the table with the force, minus the derivative of the spline */
 +            table_f[i] = -dc;
 +        }
 +        else
 +        {
 +            /* tab[i] will contain the average of the splines over the two intervals */
 +            table_f[i] += -0.5*dc;
 +        }
 +
 +        if (!bOutOfRange)
 +        {
 +            /* Make spline s(x) = a0 + a1*(x - xr) + 0.5*a2*(x - xr)^2
 +             * matching the potential at the two end points
 +             * and the derivative dc at the end point xr.
 +             */
 +            a0   = v_r0;
 +            a1   = dc;
 +            a2dx = (a1*dx + v_r1 - a0)*2/dx;
 +
 +            /* Set dc to the derivative at the next point */
 +            dc_new = a1 - a2dx;
 +
 +            if (dc_new != dc_new || dc_new < -tab_max || dc_new > tab_max)
 +            {
 +                bOutOfRange = TRUE;
 +            }
 +            else
 +            {
 +                dc = dc_new;
 +            }
 +        }
 +
 +        table_f[(i-1)] = -0.5*dc;
 +    }
 +    /* Currently the last value only contains half the force: double it */
 +    table_f[0] *= 2;
 +
 +    if (table_v != NULL && table_fdv0 != NULL)
 +    {
 +        /* Copy to FDV0 table too. Allocation occurs in forcerec.c,
 +         * init_ewald_f_table().
 +         */
 +        for (i = 0; i < ntab-1; i++)
 +        {
 +            table_fdv0[4*i]     = table_f[i];
 +            table_fdv0[4*i+1]   = table_f[i+1]-table_f[i];
 +            table_fdv0[4*i+2]   = table_v[i];
 +            table_fdv0[4*i+3]   = 0.0;
 +        }
 +        table_fdv0[4*(ntab-1)]    = table_f[(ntab-1)];
 +        table_fdv0[4*(ntab-1)+1]  = -table_f[(ntab-1)];
 +        table_fdv0[4*(ntab-1)+2]  = table_v[(ntab-1)];
 +        table_fdv0[4*(ntab-1)+3]  = 0.0;
 +    }
 +}
 +
 +/* Returns the spacing for a function using the maximum of
 + * the third derivative, x_scale (unit 1/length)
 + * and function tolerance.
 + */
 +static double spline3_table_scale(double third_deriv_max,
 +                                  double x_scale,
 +                                  double func_tol)
 +{
 +    double deriv_tol;
 +    double sc_deriv, sc_func;
 +
 +    /* Force tolerance: single precision accuracy */
 +    deriv_tol = GMX_FLOAT_EPS;
 +    sc_deriv  = sqrt(third_deriv_max/(6*4*deriv_tol*x_scale))*x_scale;
 +
 +    /* Don't try to be more accurate on energy than the precision */
 +    func_tol  = std::max(func_tol, static_cast<double>(GMX_REAL_EPS));
 +    sc_func   = std::cbrt(third_deriv_max/(6*12*sqrt(3)*func_tol))*x_scale;
 +
 +    return std::max(sc_deriv, sc_func);
 +}
 +
 +/* The scale (1/spacing) for third order spline interpolation
 + * of the Ewald mesh contribution which needs to be subtracted
 + * from the non-bonded interactions.
 + * Since there is currently only one spacing for Coulomb and LJ,
 + * the finest spacing is used if both Ewald types are present.
 + *
 + * Note that we could also implement completely separate tables
 + * for Coulomb and LJ Ewald, each with their own spacing.
 + * The current setup with the same spacing can provide slightly
 + * faster kernels with both Coulomb and LJ Ewald, especially
 + * when interleaving both tables (currently not implemented).
 + */
 +real ewald_spline3_table_scale(const interaction_const_t *ic)
 +{
 +    real sc;
 +
 +    sc = 0;
 +
 +    if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype))
 +    {
 +        double erf_x_d3 = 1.0522; /* max of (erf(x)/x)''' */
 +        double etol;
 +        real   sc_q;
 +
 +        /* Energy tolerance: 0.1 times the cut-off jump */
 +        etol  = 0.1*std::erfc(ic->ewaldcoeff_q*ic->rcoulomb);
 +
 +        sc_q  = spline3_table_scale(erf_x_d3, ic->ewaldcoeff_q, etol);
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "Ewald Coulomb quadratic spline table spacing: %f 1/nm\n", 1/sc_q);
 +        }
 +
 +        sc    = std::max(sc, sc_q);
 +    }
 +
 +    if (EVDW_PME(ic->vdwtype))
 +    {
 +        double func_d3 = 0.42888; /* max of (x^-6 (1 - exp(-x^2)(1+x^2+x^4/2)))''' */
 +        double xrc2, etol;
 +        real   sc_lj;
 +
 +        /* Energy tolerance: 0.1 times the cut-off jump */
 +        xrc2  = gmx::square(ic->ewaldcoeff_lj*ic->rvdw);
 +        etol  = 0.1*std::exp(-xrc2)*(1 + xrc2 + xrc2*xrc2/2.0);
 +
 +        sc_lj = spline3_table_scale(func_d3, ic->ewaldcoeff_lj, etol);
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "Ewald LJ quadratic spline table spacing: %f 1/nm\n", 1/sc_lj);
 +        }
 +
 +        sc = std::max(sc, sc_lj);
 +    }
 +
 +    return sc;
 +}
 +
 +/* Calculate the potential and force for an r value
 + * in exactly the same way it is done in the inner loop.
 + * VFtab is a pointer to the table data, offset is
 + * the point where we should begin and stride is
 + * 4 if we have a buckingham table, 3 otherwise.
 + * If you want to evaluate table no N, set offset to 4*N.
 + *
 + * We use normal precision here, since that is what we
 + * will use in the inner loops.
 + */
 +static void evaluate_table(real VFtab[], int offset, int stride,
 +                           real tabscale, real r, real *y, real *yp)
 +{
 +    int  n;
 +    real rt, eps, eps2;
 +    real Y, F, Geps, Heps2, Fp;
 +
 +    rt       =  r*tabscale;
 +    n        =  (int)rt;
 +    eps      =  rt - n;
 +    eps2     =  eps*eps;
 +    n        =  offset+stride*n;
 +    Y        =  VFtab[n];
 +    F        =  VFtab[n+1];
 +    Geps     =  eps*VFtab[n+2];
 +    Heps2    =  eps2*VFtab[n+3];
 +    Fp       =  F+Geps+Heps2;
 +    *y       =  Y+eps*Fp;
 +    *yp      =  (Fp+Geps+2.0*Heps2)*tabscale;
 +}
 +
 +static void copy2table(int n, int offset, int stride,
 +                       double x[], double Vtab[], double Ftab[], real scalefactor,
 +                       real dest[])
 +{
 +/* Use double prec. for the intermediary variables
 + * and temporary x/vtab/vtab2 data to avoid unnecessary
 + * loss of precision.
 + */
 +    int    i, nn0;
 +    double F, G, H, h;
 +
 +    h = 0;
 +    for (i = 0; (i < n); i++)
 +    {
 +        if (i < n-1)
 +        {
 +            h   = x[i+1] - x[i];
 +            F   = -Ftab[i]*h;
 +            G   =  3*(Vtab[i+1] - Vtab[i]) + (Ftab[i+1] + 2*Ftab[i])*h;
 +            H   = -2*(Vtab[i+1] - Vtab[i]) - (Ftab[i+1] +   Ftab[i])*h;
 +        }
 +        else
 +        {
 +            /* Fill the last entry with a linear potential,
 +             * this is mainly for rounding issues with angle and dihedral potentials.
 +             */
 +            F   = -Ftab[i]*h;
 +            G   = 0;
 +            H   = 0;
 +        }
 +        nn0         = offset + i*stride;
 +        dest[nn0]   = scalefactor*Vtab[i];
 +        dest[nn0+1] = scalefactor*F;
 +        dest[nn0+2] = scalefactor*G;
 +        dest[nn0+3] = scalefactor*H;
 +    }
 +}
 +
 +static void init_table(int n, int nx0,
 +                       double tabscale, t_tabledata *td, gmx_bool bAlloc)
 +{
 +    td->nx       = n;
 +    td->nx0      = nx0;
 +    td->tabscale = tabscale;
 +    if (bAlloc)
 +    {
 +        snew(td->x, td->nx);
 +        snew(td->v, td->nx);
 +        snew(td->f, td->nx);
 +    }
 +}
 +
 +static void spline_forces(int nx, double h, double v[], gmx_bool bS3, gmx_bool bE3,
 +                          double f[])
 +{
 +    int    start, end, i;
 +    double v3, b_s, b_e, b;
 +    double beta, *gamma;
 +
 +    /* Formulas can be found in:
 +     * H.J.C. Berendsen, Simulating the Physical World, Cambridge 2007
 +     */
 +
 +    if (nx < 4 && (bS3 || bE3))
 +    {
 +        gmx_fatal(FARGS, "Can not generate splines with third derivative boundary conditions with less than 4 (%d) points", nx);
 +    }
 +
 +    /* To make life easy we initially set the spacing to 1
 +     * and correct for this at the end.
 +     */
 +    if (bS3)
 +    {
 +        /* Fit V''' at the start */
 +        v3  = v[3] - 3*v[2] + 3*v[1] - v[0];
 +        if (debug)
 +        {
 +            fprintf(debug, "The left third derivative is %g\n", v3/(h*h*h));
 +        }
 +        b_s   = 2*(v[1] - v[0]) + v3/6;
 +        start = 0;
 +
 +        if (FALSE)
 +        {
 +            /* Fit V'' at the start */
 +            real v2;
 +
 +            v2  = -v[3] + 4*v[2] - 5*v[1] + 2*v[0];
 +            /* v2  = v[2] - 2*v[1] + v[0]; */
 +            if (debug)
 +            {
 +                fprintf(debug, "The left second derivative is %g\n", v2/(h*h));
 +            }
 +            b_s   = 3*(v[1] - v[0]) - v2/2;
 +            start = 0;
 +        }
 +    }
 +    else
 +    {
 +        b_s   = 3*(v[2] - v[0]) + f[0]*h;
 +        start = 1;
 +    }
 +    if (bE3)
 +    {
 +        /* Fit V''' at the end */
 +        v3  = v[nx-1] - 3*v[nx-2] + 3*v[nx-3] - v[nx-4];
 +        if (debug)
 +        {
 +            fprintf(debug, "The right third derivative is %g\n", v3/(h*h*h));
 +        }
 +        b_e = 2*(v[nx-1] - v[nx-2]) + v3/6;
 +        end = nx;
 +    }
 +    else
 +    {
 +        /* V'=0 at the end */
 +        b_e = 3*(v[nx-1] - v[nx-3]) + f[nx-1]*h;
 +        end = nx - 1;
 +    }
 +
 +    snew(gamma, nx);
 +    beta = (bS3 ? 1 : 4);
 +
 +    /* For V'' fitting */
 +    /* beta = (bS3 ? 2 : 4); */
 +
 +    f[start] = b_s/beta;
 +    for (i = start+1; i < end; i++)
 +    {
 +        gamma[i] = 1/beta;
 +        beta     = 4 - gamma[i];
 +        b        =  3*(v[i+1] - v[i-1]);
 +        f[i]     = (b - f[i-1])/beta;
 +    }
 +    gamma[end-1] = 1/beta;
 +    beta         = (bE3 ? 1 : 4) - gamma[end-1];
 +    f[end-1]     = (b_e - f[end-2])/beta;
 +
 +    for (i = end-2; i >= start; i--)
 +    {
 +        f[i] -= gamma[i+1]*f[i+1];
 +    }
 +    sfree(gamma);
 +
 +    /* Correct for the minus sign and the spacing */
 +    for (i = start; i < end; i++)
 +    {
 +        f[i] = -f[i]/h;
 +    }
 +}
 +
 +static void set_forces(FILE *fp, int angle,
 +                       int nx, double h, double v[], double f[],
 +                       int table)
 +{
 +    int start, end;
 +
 +    if (angle == 2)
 +    {
 +        gmx_fatal(FARGS,
 +                  "Force generation for dihedral tables is not (yet) implemented");
 +    }
 +
 +    start = 0;
 +    while (v[start] == 0)
 +    {
 +        start++;
 +    }
 +
 +    end = nx;
 +    while (v[end-1] == 0)
 +    {
 +        end--;
 +    }
 +    if (end > nx - 2)
 +    {
 +        end = nx;
 +    }
 +    else
 +    {
 +        end++;
 +    }
 +
 +    if (fp)
 +    {
 +        fprintf(fp, "Generating forces for table %d, boundary conditions: V''' at %g, %s at %g\n",
 +                table+1, start*h, end == nx ? "V'''" : "V'=0", (end-1)*h);
 +    }
 +    spline_forces(end-start, h, v+start, TRUE, end == nx, f+start);
 +}
 +
 +static void read_tables(FILE *fp, const char *fn,
 +                        int ntab, int angle, t_tabledata td[])
 +{
 +    char    *libfn;
 +    char     buf[STRLEN];
 +    double **yy = NULL, start, end, dx0, dx1, ssd, vm, vp, f, numf;
 +    int      k, i, nx, nx0 = 0, ny, nny, ns;
 +    gmx_bool bAllZero, bZeroV, bZeroF;
 +    double   tabscale;
 +
 +    nny   = 2*ntab+1;
 +    libfn = gmxlibfn(fn);
 +    nx    = read_xvg(libfn, &yy, &ny);
 +    if (ny != nny)
 +    {
 +        gmx_fatal(FARGS, "Trying to read file %s, but nr columns = %d, should be %d",
 +                  libfn, ny, nny);
 +    }
 +    if (angle == 0)
 +    {
 +        if (yy[0][0] != 0.0)
 +        {
 +            gmx_fatal(FARGS,
 +                      "The first distance in file %s is %f nm instead of %f nm",
 +                      libfn, yy[0][0], 0.0);
 +        }
 +    }
 +    else
 +    {
 +        if (angle == 1)
 +        {
 +            start = 0.0;
 +        }
 +        else
 +        {
 +            start = -180.0;
 +        }
 +        end = 180.0;
 +        if (yy[0][0] != start || yy[0][nx-1] != end)
 +        {
 +            gmx_fatal(FARGS, "The angles in file %s should go from %f to %f instead of %f to %f\n",
 +                      libfn, start, end, yy[0][0], yy[0][nx-1]);
 +        }
 +    }
 +
 +    tabscale = (nx-1)/(yy[0][nx-1] - yy[0][0]);
 +
 +    if (fp)
 +    {
 +        fprintf(fp, "Read user tables from %s with %d data points.\n", libfn, nx);
 +        if (angle == 0)
 +        {
 +            fprintf(fp, "Tabscale = %g points/nm\n", tabscale);
 +        }
 +    }
 +
 +    bAllZero = TRUE;
 +    for (k = 0; k < ntab; k++)
 +    {
 +        bZeroV = TRUE;
 +        bZeroF = TRUE;
 +        for (i = 0; (i < nx); i++)
 +        {
 +            if (i >= 2)
 +            {
 +                dx0 = yy[0][i-1] - yy[0][i-2];
 +                dx1 = yy[0][i]   - yy[0][i-1];
 +                /* Check for 1% deviation in spacing */
 +                if (fabs(dx1 - dx0) >= 0.005*(fabs(dx0) + fabs(dx1)))
 +                {
 +                    gmx_fatal(FARGS, "In table file '%s' the x values are not equally spaced: %f %f %f", fn, yy[0][i-2], yy[0][i-1], yy[0][i]);
 +                }
 +            }
 +            if (yy[1+k*2][i] != 0)
 +            {
 +                bZeroV = FALSE;
 +                if (bAllZero)
 +                {
 +                    bAllZero = FALSE;
 +                    nx0      = i;
 +                }
 +                if (yy[1+k*2][i] >  0.01*GMX_REAL_MAX ||
 +                    yy[1+k*2][i] < -0.01*GMX_REAL_MAX)
 +                {
 +                    gmx_fatal(FARGS, "Out of range potential value %g in file '%s'",
 +                              yy[1+k*2][i], fn);
 +                }
 +            }
 +            if (yy[1+k*2+1][i] != 0)
 +            {
 +                bZeroF = FALSE;
 +                if (bAllZero)
 +                {
 +                    bAllZero = FALSE;
 +                    nx0      = i;
 +                }
 +                if (yy[1+k*2+1][i] >  0.01*GMX_REAL_MAX ||
 +                    yy[1+k*2+1][i] < -0.01*GMX_REAL_MAX)
 +                {
 +                    gmx_fatal(FARGS, "Out of range force value %g in file '%s'",
 +                              yy[1+k*2+1][i], fn);
 +                }
 +            }
 +        }
 +
 +        if (!bZeroV && bZeroF)
 +        {
 +            set_forces(fp, angle, nx, 1/tabscale, yy[1+k*2], yy[1+k*2+1], k);
 +        }
 +        else
 +        {
 +            /* Check if the second column is close to minus the numerical
 +             * derivative of the first column.
 +             */
 +            ssd = 0;
 +            ns  = 0;
 +            for (i = 1; (i < nx-1); i++)
 +            {
 +                vm = yy[1+2*k][i-1];
 +                vp = yy[1+2*k][i+1];
 +                f  = yy[1+2*k+1][i];
 +                if (vm != 0 && vp != 0 && f != 0)
 +                {
 +                    /* Take the centered difference */
 +                    numf = -(vp - vm)*0.5*tabscale;
-                 sprintf(buf, "For the %d non-zero entries for table %d in %s the forces deviate on average %d%% from minus the numerical derivative of the potential\n", ns, k, libfn, (int)(100*ssd+0.5));
++                    if (f + numf != 0)
++                    {
++                        ssd += fabs(2*(f - numf)/(f + numf));
++                    }
 +                    ns++;
 +                }
 +            }
 +            if (ns > 0)
 +            {
 +                ssd /= ns;
- bondedtable_t make_bonded_table(FILE *fplog, char *fn, int angle)
++                sprintf(buf, "For the %d non-zero entries for table %d in %s the forces deviate on average %lld%% from minus the numerical derivative of the potential\n", ns, k, libfn, (long long int)(100*ssd+0.5));
 +                if (debug)
 +                {
 +                    fprintf(debug, "%s", buf);
 +                }
 +                if (ssd > 0.2)
 +                {
 +                    if (fp)
 +                    {
 +                        fprintf(fp, "\nWARNING: %s\n", buf);
 +                    }
 +                    fprintf(stderr, "\nWARNING: %s\n", buf);
 +                }
 +            }
 +        }
 +    }
 +    if (bAllZero && fp)
 +    {
 +        fprintf(fp, "\nNOTE: All elements in table %s are zero\n\n", libfn);
 +    }
 +
 +    for (k = 0; (k < ntab); k++)
 +    {
 +        init_table(nx, nx0, tabscale, &(td[k]), TRUE);
 +        for (i = 0; (i < nx); i++)
 +        {
 +            td[k].x[i] = yy[0][i];
 +            td[k].v[i] = yy[2*k+1][i];
 +            td[k].f[i] = yy[2*k+2][i];
 +        }
 +    }
 +    for (i = 0; (i < ny); i++)
 +    {
 +        sfree(yy[i]);
 +    }
 +    sfree(yy);
 +    sfree(libfn);
 +}
 +
 +static void done_tabledata(t_tabledata *td)
 +{
 +    if (!td)
 +    {
 +        return;
 +    }
 +
 +    sfree(td->x);
 +    sfree(td->v);
 +    sfree(td->f);
 +}
 +
 +static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr,
 +                       gmx_bool b14only)
 +{
 +    /* Fill the table according to the formulas in the manual.
 +     * In principle, we only need the potential and the second
 +     * derivative, but then we would have to do lots of calculations
 +     * in the inner loop. By precalculating some terms (see manual)
 +     * we get better eventual performance, despite a larger table.
 +     *
 +     * Since some of these higher-order terms are very small,
 +     * we always use double precision to calculate them here, in order
 +     * to avoid unnecessary loss of precision.
 +     */
 +    int      i;
 +    double   reppow, p;
 +    double   r1, rc, r12, r13;
 +    double   r, r2, r6, rc2, rc6, rc12;
 +    double   expr, Vtab, Ftab;
 +    /* Parameters for David's function */
 +    double   A = 0, B = 0, C = 0, A_3 = 0, B_4 = 0;
 +    /* Parameters for the switching function */
 +    double   ksw, swi, swi1;
 +    /* Temporary parameters */
 +    gmx_bool bPotentialSwitch, bForceSwitch, bPotentialShift;
 +    double   ewc   = fr->ewaldcoeff_q;
 +    double   ewclj = fr->ewaldcoeff_lj;
 +    double   Vcut  = 0;
 +
 +    if (b14only)
 +    {
 +        bPotentialSwitch = FALSE;
 +        bForceSwitch     = FALSE;
 +        bPotentialShift  = FALSE;
 +    }
 +    else
 +    {
 +        bPotentialSwitch = ((tp == etabLJ6Switch) || (tp == etabLJ12Switch) ||
 +                            (tp == etabCOULSwitch) ||
 +                            (tp == etabEwaldSwitch) || (tp == etabEwaldUserSwitch) ||
 +                            (tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodPOTSWITCH)) ||
 +                            (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodPOTSWITCH)));
 +        bForceSwitch  = ((tp == etabLJ6Shift) || (tp == etabLJ12Shift) ||
 +                         (tp == etabShift) ||
 +                         (tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodFORCESWITCH)) ||
 +                         (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodFORCESWITCH)));
 +        bPotentialShift = ((tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodPOTSHIFT)) ||
 +                           (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodPOTSHIFT)));
 +    }
 +
 +    reppow = fr->reppow;
 +
 +    if (tprops[tp].bCoulomb)
 +    {
 +        r1 = fr->rcoulomb_switch;
 +        rc = fr->rcoulomb;
 +    }
 +    else
 +    {
 +        r1 = fr->rvdw_switch;
 +        rc = fr->rvdw;
 +    }
 +    if (bPotentialSwitch)
 +    {
 +        ksw  = 1.0/(gmx::power5(rc-r1));
 +    }
 +    else
 +    {
 +        ksw  = 0.0;
 +    }
 +    if (bForceSwitch)
 +    {
 +        if (tp == etabShift)
 +        {
 +            p = 1;
 +        }
 +        else if (tp == etabLJ6Shift)
 +        {
 +            p = 6;
 +        }
 +        else
 +        {
 +            p = reppow;
 +        }
 +
 +        A = p * ((p+1)*r1-(p+4)*rc)/(std::pow(rc, p+2)*gmx::square(rc-r1));
 +        B = -p * ((p+1)*r1-(p+3)*rc)/(std::pow(rc, p+2)*gmx::power3(rc-r1));
 +        C = 1.0/std::pow(rc, p)-A/3.0*gmx::power3(rc-r1)-B/4.0*gmx::power4(rc-r1);
 +        if (tp == etabLJ6Shift)
 +        {
 +            A = -A;
 +            B = -B;
 +            C = -C;
 +        }
 +        A_3 = A/3.0;
 +        B_4 = B/4.0;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Setting up tables\n"); fflush(debug);
 +    }
 +
 +    if (bPotentialShift)
 +    {
 +        rc2   = rc*rc;
 +        rc6   = 1.0/(rc2*rc2*rc2);
 +        if (gmx_within_tol(reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        {
 +            rc12 = rc6*rc6;
 +        }
 +        else
 +        {
 +            rc12 = std::pow(rc, -reppow);
 +        }
 +
 +        switch (tp)
 +        {
 +            case etabLJ6:
 +                /* Dispersion */
 +                Vcut = -rc6;
 +                break;
 +            case etabLJ6Ewald:
 +                Vcut  = -rc6*exp(-ewclj*ewclj*rc2)*(1 + ewclj*ewclj*rc2 + gmx::power4(ewclj)*rc2*rc2/2);
 +                break;
 +            case etabLJ12:
 +                /* Repulsion */
 +                Vcut  = rc12;
 +                break;
 +            case etabCOUL:
 +                Vcut  = 1.0/rc;
 +                break;
 +            case etabEwald:
 +            case etabEwaldSwitch:
 +                Vcut  = std::erfc(ewc*rc)/rc;
 +                break;
 +            case etabEwaldUser:
 +                /* Only calculate minus the reciprocal space contribution */
 +                Vcut  = -std::erf(ewc*rc)/rc;
 +                break;
 +            case etabRF:
 +            case etabRF_ZERO:
 +                /* No need for preventing the usage of modifiers with RF */
 +                Vcut  = 0.0;
 +                break;
 +            case etabEXPMIN:
 +                Vcut  = exp(-rc);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Cannot apply new potential-shift modifier to interaction type '%s' yet. (%s,%d)",
 +                          tprops[tp].name, __FILE__, __LINE__);
 +        }
 +    }
 +
 +    for (i = 0; (i < td->nx); i++)
 +    {
 +        td->x[i] = i/td->tabscale;
 +    }
 +    for (i = td->nx0; (i < td->nx); i++)
 +    {
 +        r     = td->x[i];
 +        r2    = r*r;
 +        r6    = 1.0/(r2*r2*r2);
 +        if (gmx_within_tol(reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        {
 +            r12 = r6*r6;
 +        }
 +        else
 +        {
 +            r12 = std::pow(r, -reppow);
 +        }
 +        Vtab  = 0.0;
 +        Ftab  = 0.0;
 +        if (bPotentialSwitch)
 +        {
 +            /* swi is function, swi1 1st derivative and swi2 2nd derivative */
 +            /* The switch function is 1 for r<r1, 0 for r>rc, and smooth for
 +             * r1<=r<=rc. The 1st and 2nd derivatives are both zero at
 +             * r1 and rc.
 +             * ksw is just the constant 1/(rc-r1)^5, to save some calculations...
 +             */
 +            if (r <= r1)
 +            {
 +                swi  = 1.0;
 +                swi1 = 0.0;
 +            }
 +            else if (r >= rc)
 +            {
 +                swi  = 0.0;
 +                swi1 = 0.0;
 +            }
 +            else
 +            {
 +                swi      = 1 - 10*gmx::power3(r-r1)*ksw*gmx::square(rc-r1)
 +                    + 15*gmx::power4(r-r1)*ksw*(rc-r1) - 6*gmx::power5(r-r1)*ksw;
 +                swi1     = -30*gmx::square(r-r1)*ksw*gmx::square(rc-r1)
 +                    + 60*gmx::power3(r-r1)*ksw*(rc-r1) - 30*gmx::power4(r-r1)*ksw;
 +            }
 +        }
 +        else /* not really needed, but avoids compiler warnings... */
 +        {
 +            swi  = 1.0;
 +            swi1 = 0.0;
 +        }
 +
 +        rc6 = rc*rc*rc;
 +        rc6 = 1.0/(rc6*rc6);
 +
 +        switch (tp)
 +        {
 +            case etabLJ6:
 +                /* Dispersion */
 +                Vtab = -r6;
 +                Ftab = 6.0*Vtab/r;
 +                break;
 +            case etabLJ6Switch:
 +            case etabLJ6Shift:
 +                /* Dispersion */
 +                if (r < rc)
 +                {
 +                    Vtab = -r6;
 +                    Ftab = 6.0*Vtab/r;
 +                    break;
 +                }
 +                break;
 +            case etabLJ12:
 +                /* Repulsion */
 +                Vtab  = r12;
 +                Ftab  = reppow*Vtab/r;
 +                break;
 +            case etabLJ12Switch:
 +            case etabLJ12Shift:
 +                /* Repulsion */
 +                if (r < rc)
 +                {
 +                    Vtab  = r12;
 +                    Ftab  = reppow*Vtab/r;
 +                }
 +                break;
 +            case etabLJ6Encad:
 +                if (r < rc)
 +                {
 +                    Vtab  = -(r6-6.0*(rc-r)*rc6/rc-rc6);
 +                    Ftab  = -(6.0*r6/r-6.0*rc6/rc);
 +                }
 +                else /* r>rc */
 +                {
 +                    Vtab  = 0;
 +                    Ftab  = 0;
 +                }
 +                break;
 +            case etabLJ12Encad:
 +                if (r < rc)
 +                {
 +                    Vtab  = -(r6-6.0*(rc-r)*rc6/rc-rc6);
 +                    Ftab  = -(6.0*r6/r-6.0*rc6/rc);
 +                }
 +                else /* r>rc */
 +                {
 +                    Vtab  = 0;
 +                    Ftab  = 0;
 +                }
 +                break;
 +            case etabCOUL:
 +                Vtab  = 1.0/r;
 +                Ftab  = 1.0/r2;
 +                break;
 +            case etabCOULSwitch:
 +            case etabShift:
 +                if (r < rc)
 +                {
 +                    Vtab  = 1.0/r;
 +                    Ftab  = 1.0/r2;
 +                }
 +                break;
 +            case etabEwald:
 +            case etabEwaldSwitch:
 +                Vtab  = std::erfc(ewc*r)/r;
 +                Ftab  = std::erfc(ewc*r)/r2+exp(-(ewc*ewc*r2))*ewc*M_2_SQRTPI/r;
 +                break;
 +            case etabEwaldUser:
 +            case etabEwaldUserSwitch:
 +                /* Only calculate the negative of the reciprocal space contribution */
 +                Vtab  = -std::erf(ewc*r)/r;
 +                Ftab  = -std::erf(ewc*r)/r2+exp(-(ewc*ewc*r2))*ewc*M_2_SQRTPI/r;
 +                break;
 +            case etabLJ6Ewald:
 +                Vtab  = -r6*exp(-ewclj*ewclj*r2)*(1 + ewclj*ewclj*r2 + gmx::power4(ewclj)*r2*r2/2);
 +                Ftab  = 6.0*Vtab/r - r6*exp(-ewclj*ewclj*r2)*gmx::power5(ewclj)*ewclj*r2*r2*r;
 +                break;
 +            case etabRF:
 +            case etabRF_ZERO:
 +                Vtab  = 1.0/r      +   fr->k_rf*r2 - fr->c_rf;
 +                Ftab  = 1.0/r2     - 2*fr->k_rf*r;
 +                if (tp == etabRF_ZERO && r >= rc)
 +                {
 +                    Vtab = 0;
 +                    Ftab = 0;
 +                }
 +                break;
 +            case etabEXPMIN:
 +                expr  = exp(-r);
 +                Vtab  = expr;
 +                Ftab  = expr;
 +                break;
 +            case etabCOULEncad:
 +                if (r < rc)
 +                {
 +                    Vtab  = 1.0/r-(rc-r)/(rc*rc)-1.0/rc;
 +                    Ftab  = 1.0/r2-1.0/(rc*rc);
 +                }
 +                else /* r>rc */
 +                {
 +                    Vtab  = 0;
 +                    Ftab  = 0;
 +                }
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Table type %d not implemented yet. (%s,%d)",
 +                          tp, __FILE__, __LINE__);
 +        }
 +        if (bForceSwitch)
 +        {
 +            /* Normal coulomb with cut-off correction for potential */
 +            if (r < rc)
 +            {
 +                Vtab -= C;
 +                /* If in Shifting range add something to it */
 +                if (r > r1)
 +                {
 +                    r12    = (r-r1)*(r-r1);
 +                    r13    = (r-r1)*r12;
 +                    Vtab  += -A_3*r13 - B_4*r12*r12;
 +                    Ftab  +=   A*r12 + B*r13;
 +                }
 +            }
 +            else
 +            {
 +                /* Make sure interactions are zero outside cutoff with modifiers */
 +                Vtab = 0;
 +                Ftab = 0;
 +            }
 +        }
 +        if (bPotentialShift)
 +        {
 +            if (r < rc)
 +            {
 +                Vtab -= Vcut;
 +            }
 +            else
 +            {
 +                /* Make sure interactions are zero outside cutoff with modifiers */
 +                Vtab = 0;
 +                Ftab = 0;
 +            }
 +        }
 +
 +        if (ETAB_USER(tp))
 +        {
 +            Vtab += td->v[i];
 +            Ftab += td->f[i];
 +        }
 +
 +        if (bPotentialSwitch)
 +        {
 +            if (r >= rc)
 +            {
 +                /* Make sure interactions are zero outside cutoff with modifiers */
 +                Vtab = 0;
 +                Ftab = 0;
 +            }
 +            else if (r > r1)
 +            {
 +                Ftab = Ftab*swi - Vtab*swi1;
 +                Vtab = Vtab*swi;
 +            }
 +        }
 +        /* Convert to single precision when we store to mem */
 +        td->v[i]  = Vtab;
 +        td->f[i]  = Ftab;
 +    }
 +
 +    /* Continue the table linearly from nx0 to 0.
 +     * These values are only required for energy minimization with overlap or TPI.
 +     */
 +    for (i = td->nx0-1; i >= 0; i--)
 +    {
 +        td->v[i] = td->v[i+1] + td->f[i+1]*(td->x[i+1] - td->x[i]);
 +        td->f[i] = td->f[i+1];
 +    }
 +}
 +
 +static void set_table_type(int tabsel[], const t_forcerec *fr, gmx_bool b14only)
 +{
 +    int eltype, vdwtype;
 +
 +    /* Set the different table indices.
 +     * Coulomb first.
 +     */
 +
 +
 +    if (b14only)
 +    {
 +        switch (fr->eeltype)
 +        {
 +            case eelUSER:
 +            case eelPMEUSER:
 +            case eelPMEUSERSWITCH:
 +                eltype = eelUSER;
 +                break;
 +            default:
 +                eltype = eelCUT;
 +        }
 +    }
 +    else
 +    {
 +        eltype = fr->eeltype;
 +    }
 +
 +    switch (eltype)
 +    {
 +        case eelCUT:
 +            tabsel[etiCOUL] = etabCOUL;
 +            break;
 +        case eelPOISSON:
 +            tabsel[etiCOUL] = etabShift;
 +            break;
 +        case eelSHIFT:
 +            if (fr->rcoulomb > fr->rcoulomb_switch)
 +            {
 +                tabsel[etiCOUL] = etabShift;
 +            }
 +            else
 +            {
 +                tabsel[etiCOUL] = etabCOUL;
 +            }
 +            break;
 +        case eelEWALD:
 +        case eelPME:
 +        case eelP3M_AD:
 +            tabsel[etiCOUL] = etabEwald;
 +            break;
 +        case eelPMESWITCH:
 +            tabsel[etiCOUL] = etabEwaldSwitch;
 +            break;
 +        case eelPMEUSER:
 +            tabsel[etiCOUL] = etabEwaldUser;
 +            break;
 +        case eelPMEUSERSWITCH:
 +            tabsel[etiCOUL] = etabEwaldUserSwitch;
 +            break;
 +        case eelRF:
 +        case eelGRF:
 +        case eelRF_ZERO:
 +            tabsel[etiCOUL] = etabRF_ZERO;
 +            break;
 +        case eelSWITCH:
 +            tabsel[etiCOUL] = etabCOULSwitch;
 +            break;
 +        case eelUSER:
 +            tabsel[etiCOUL] = etabUSER;
 +            break;
 +        case eelENCADSHIFT:
 +            tabsel[etiCOUL] = etabCOULEncad;
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Invalid eeltype %d", eltype);
 +    }
 +
 +    /* Van der Waals time */
 +    if (fr->bBHAM && !b14only)
 +    {
 +        tabsel[etiLJ6]  = etabLJ6;
 +        tabsel[etiLJ12] = etabEXPMIN;
 +    }
 +    else
 +    {
 +        if (b14only && fr->vdwtype != evdwUSER)
 +        {
 +            vdwtype = evdwCUT;
 +        }
 +        else
 +        {
 +            vdwtype = fr->vdwtype;
 +        }
 +
 +        switch (vdwtype)
 +        {
 +            case evdwSWITCH:
 +                tabsel[etiLJ6]  = etabLJ6Switch;
 +                tabsel[etiLJ12] = etabLJ12Switch;
 +                break;
 +            case evdwSHIFT:
 +                tabsel[etiLJ6]  = etabLJ6Shift;
 +                tabsel[etiLJ12] = etabLJ12Shift;
 +                break;
 +            case evdwUSER:
 +                tabsel[etiLJ6]  = etabUSER;
 +                tabsel[etiLJ12] = etabUSER;
 +                break;
 +            case evdwCUT:
 +                tabsel[etiLJ6]  = etabLJ6;
 +                tabsel[etiLJ12] = etabLJ12;
 +                break;
 +            case evdwENCADSHIFT:
 +                tabsel[etiLJ6]  = etabLJ6Encad;
 +                tabsel[etiLJ12] = etabLJ12Encad;
 +                break;
 +            case evdwPME:
 +                tabsel[etiLJ6]  = etabLJ6Ewald;
 +                tabsel[etiLJ12] = etabLJ12;
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Invalid vdwtype %d in %s line %d", vdwtype,
 +                          __FILE__, __LINE__);
 +        }
 +
 +        if (!b14only && fr->vdw_modifier != eintmodNONE)
 +        {
 +            if (fr->vdw_modifier != eintmodPOTSHIFT &&
 +                fr->vdwtype != evdwCUT)
 +            {
 +                gmx_incons("Potential modifiers other than potential-shift are only implemented for LJ cut-off");
 +            }
 +
 +            /* LJ-PME and other (shift-only) modifiers are handled by applying the modifiers
 +             * to the original interaction forms when we fill the table, so we only check cutoffs here.
 +             */
 +            if (fr->vdwtype == evdwCUT)
 +            {
 +                switch (fr->vdw_modifier)
 +                {
 +                    case eintmodNONE:
 +                    case eintmodPOTSHIFT:
 +                    case eintmodEXACTCUTOFF:
 +                        /* No modification */
 +                        break;
 +                    case eintmodPOTSWITCH:
 +                        tabsel[etiLJ6]  = etabLJ6Switch;
 +                        tabsel[etiLJ12] = etabLJ12Switch;
 +                        break;
 +                    case eintmodFORCESWITCH:
 +                        tabsel[etiLJ6]  = etabLJ6Shift;
 +                        tabsel[etiLJ12] = etabLJ12Shift;
 +                        break;
 +                    default:
 +                        gmx_incons("Unsupported vdw_modifier");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +t_forcetable *make_tables(FILE *out,
 +                          const t_forcerec *fr,
 +                          const char *fn,
 +                          real rtab, int flags)
 +{
 +    t_tabledata    *td;
 +    gmx_bool        b14only, useUserTable;
 +    int             nx0, tabsel[etiNR];
 +    real            scalefactor;
 +
 +    t_forcetable   *table;
 +
 +    snew(table, 1);
 +    b14only = (flags & GMX_MAKETABLES_14ONLY);
 +
 +    if (flags & GMX_MAKETABLES_FORCEUSER)
 +    {
 +        tabsel[etiCOUL] = etabUSER;
 +        tabsel[etiLJ6]  = etabUSER;
 +        tabsel[etiLJ12] = etabUSER;
 +    }
 +    else
 +    {
 +        set_table_type(tabsel, fr, b14only);
 +    }
 +    snew(td, etiNR);
 +    table->r         = rtab;
 +    table->scale     = 0;
 +    table->n         = 0;
 +
 +    table->interaction   = GMX_TABLE_INTERACTION_ELEC_VDWREP_VDWDISP;
 +    table->format        = GMX_TABLE_FORMAT_CUBICSPLINE_YFGH;
 +    table->formatsize    = 4;
 +    table->ninteractions = etiNR;
 +    table->stride        = table->formatsize*table->ninteractions;
 +
 +    /* Check whether we have to read or generate */
 +    useUserTable = FALSE;
 +    for (unsigned int i = 0; (i < etiNR); i++)
 +    {
 +        if (ETAB_USER(tabsel[i]))
 +        {
 +            useUserTable = TRUE;
 +        }
 +    }
 +    if (useUserTable)
 +    {
 +        read_tables(out, fn, etiNR, 0, td);
 +        if (rtab == 0 || (flags & GMX_MAKETABLES_14ONLY))
 +        {
 +            table->n = td[0].nx;
 +        }
 +        else
 +        {
 +            if (td[0].x[td[0].nx-1] < rtab)
 +            {
 +                gmx_fatal(FARGS, "Tables in file %s not long enough for cut-off:\n"
 +                          "\tshould be at least %f nm\n", fn, rtab);
 +            }
 +            table->n = (int)(rtab*td[0].tabscale + 0.5);
 +        }
 +        table->scale = td[0].tabscale;
 +        nx0          = td[0].nx0;
 +    }
 +    else
 +    {
 +        // No tables are read
 +#if GMX_DOUBLE
 +        table->scale = 2000.0;
 +#else
 +        table->scale = 500.0;
 +#endif
 +        table->n = static_cast<int>(rtab*table->scale);
 +        nx0      = 10;
 +    }
 +
 +    /* Each table type (e.g. coul,lj6,lj12) requires four
 +     * numbers per table->n+1 data points. For performance reasons we want
 +     * the table data to be aligned to a 32-byte boundary.
 +     */
 +    snew_aligned(table->data, table->stride*(table->n+1)*sizeof(real), 32);
 +
 +    for (int k = 0; (k < etiNR); k++)
 +    {
 +        /* Now fill data for tables that should not be read (perhaps
 +           overwriting data that was read but should not be used) */
 +        if (!ETAB_USER(tabsel[k]))
 +        {
 +            real scale = table->scale;
 +            if (fr->bBHAM && (fr->bham_b_max != 0) && tabsel[k] == etabEXPMIN)
 +            {
 +                scale /= fr->bham_b_max;
 +            }
 +            init_table(table->n, nx0, scale, &(td[k]), !useUserTable);
 +
 +            fill_table(&(td[k]), tabsel[k], fr, b14only);
 +            if (out)
 +            {
 +                fprintf(out, "Generated table with %d data points for %s%s.\n"
 +                        "Tabscale = %g points/nm\n",
 +                        td[k].nx, b14only ? "1-4 " : "", tprops[tabsel[k]].name,
 +                        td[k].tabscale);
 +            }
 +        }
 +
 +        /* Set scalefactor for c6/c12 tables. This is because we save flops in the non-table kernels
 +         * by including the derivative constants (6.0 or 12.0) in the parameters, since
 +         * we no longer calculate force in most steps. This means the c6/c12 parameters
 +         * have been scaled up, so we need to scale down the table interactions too.
 +         * It comes here since we need to scale user tables too.
 +         */
 +        if (k == etiLJ6)
 +        {
 +            scalefactor = 1.0/6.0;
 +        }
 +        else if (k == etiLJ12 && tabsel[k] != etabEXPMIN)
 +        {
 +            scalefactor = 1.0/12.0;
 +        }
 +        else
 +        {
 +            scalefactor = 1.0;
 +        }
 +
 +        copy2table(table->n, k*table->formatsize, table->stride, td[k].x, td[k].v, td[k].f, scalefactor, table->data);
 +
 +        done_tabledata(&(td[k]));
 +    }
 +    sfree(td);
 +
 +    return table;
 +}
 +
 +t_forcetable *make_gb_table(const t_forcerec              *fr)
 +{
 +    t_tabledata    *td;
 +    int             nx0;
 +    double          r, r2, Vtab, Ftab, expterm;
 +
 +    t_forcetable   *table;
 +
 +    /* Set the table dimensions for GB, not really necessary to
 +     * use etiNR (since we only have one table, but ...)
 +     */
 +    snew(table, 1);
 +    snew(td, 1);
 +    table->interaction   = GMX_TABLE_INTERACTION_ELEC;
 +    table->format        = GMX_TABLE_FORMAT_CUBICSPLINE_YFGH;
 +    table->r             = fr->gbtabr;
 +    table->scale         = fr->gbtabscale;
 +    table->n             = static_cast<int>(table->scale*table->r);
 +    table->formatsize    = 4;
 +    table->ninteractions = 1;
 +    table->stride        = table->formatsize*table->ninteractions;
 +    nx0                  = 0;
 +
 +    /* Each table type (e.g. coul,lj6,lj12) requires four numbers per
 +     * datapoint. For performance reasons we want the table data to be
 +     * aligned on a 32-byte boundary. This new pointer must not be
 +     * used in a free() call, but thankfully we're sloppy enough not
 +     * to do this :-)
 +     */
 +
 +    snew_aligned(table->data, table->stride*table->n, 32);
 +
 +    init_table(table->n, nx0, table->scale, &(td[0]), TRUE);
 +
 +    /* Local implementation so we don't have to use the etabGB
 +     * enum above, which will cause problems later when
 +     * making the other tables (right now even though we are using
 +     * GB, the normal Coulomb tables will be created, but this
 +     * will cause a problem since fr->eeltype==etabGB which will not
 +     * be defined in fill_table and set_table_type
 +     */
 +
 +    for (int i = nx0; i < table->n; i++)
 +    {
 +        r       = td->x[i];
 +        r2      = r*r;
 +        expterm = exp(-0.25*r2);
 +
 +        Vtab = 1/sqrt(r2+expterm);
 +        Ftab = (r-0.25*r*expterm)/((r2+expterm)*sqrt(r2+expterm));
 +
 +        /* Convert to single precision when we store to mem */
 +        td->x[i]  = i/table->scale;
 +        td->v[i]  = Vtab;
 +        td->f[i]  = Ftab;
 +
 +    }
 +
 +    copy2table(table->n, 0, table->stride, td[0].x, td[0].v, td[0].f, 1.0, table->data);
 +
 +    done_tabledata(&(td[0]));
 +    sfree(td);
 +
 +    return table;
 +
 +
 +}
 +
++bondedtable_t make_bonded_table(FILE *fplog, const char *fn, int angle)
 +{
 +    t_tabledata   td;
 +    int           i;
 +    bondedtable_t tab;
 +    int           stride = 4;
 +
 +    read_tables(fplog, fn, 1, angle, &td);
 +    if (angle > 0)
 +    {
 +        /* Convert the table from degrees to radians */
 +        for (i = 0; i < td.nx; i++)
 +        {
 +            td.x[i] *= DEG2RAD;
 +            td.f[i] *= RAD2DEG;
 +        }
 +        td.tabscale *= RAD2DEG;
 +    }
 +    tab.n     = td.nx;
 +    tab.scale = td.tabscale;
 +    snew(tab.data, tab.n*stride);
 +    copy2table(tab.n, 0, stride, td.x, td.v, td.f, 1.0, tab.data);
 +    done_tabledata(&td);
 +
 +    return tab;
 +}
 +
 +t_forcetable *makeDispersionCorrectionTable(FILE *fp,
 +                                            t_forcerec *fr, real rtab,
 +                                            const char *tabfn)
 +{
 +    t_forcetable *dispersionCorrectionTable = NULL;
 +
 +    if (tabfn == NULL)
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "No table file name passed, can not read table, can not do non-bonded interactions\n");
 +        }
 +        return dispersionCorrectionTable;
 +    }
 +
 +    t_forcetable *fullTable = make_tables(fp, fr, tabfn, rtab, 0);
 +    /* Copy the contents of the table to one that has just dispersion
 +     * and repulsion, to improve cache performance. We want the table
 +     * data to be aligned to 32-byte boundaries. The pointers could be
 +     * freed but currently aren't. */
 +    snew(dispersionCorrectionTable, 1);
 +    dispersionCorrectionTable->interaction   = GMX_TABLE_INTERACTION_VDWREP_VDWDISP;
 +    dispersionCorrectionTable->format        = fullTable->format;
 +    dispersionCorrectionTable->r             = fullTable->r;
 +    dispersionCorrectionTable->n             = fullTable->n;
 +    dispersionCorrectionTable->scale         = fullTable->scale;
 +    dispersionCorrectionTable->formatsize    = fullTable->formatsize;
 +    dispersionCorrectionTable->ninteractions = 2;
 +    dispersionCorrectionTable->stride        = dispersionCorrectionTable->formatsize * dispersionCorrectionTable->ninteractions;
 +    snew_aligned(dispersionCorrectionTable->data, dispersionCorrectionTable->stride*(dispersionCorrectionTable->n+1), 32);
 +
 +    for (int i = 0; i <= fullTable->n; i++)
 +    {
 +        for (int j = 0; j < 8; j++)
 +        {
 +            dispersionCorrectionTable->data[8*i+j] = fullTable->data[12*i+4+j];
 +        }
 +    }
 +    sfree_aligned(fullTable->data);
 +    sfree(fullTable);
 +
 +    return dispersionCorrectionTable;
 +}
index 7e68fdaf2c2375ec8edcdce74c671b40d6004eb4,0000000000000000000000000000000000000000..9236b344057acce92dfd6264182329f8858cbb73
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,101 @@@
- bondedtable_t make_bonded_table(FILE *fplog, char *fn, int angle);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2012,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifndef GMX_TABLES_FORCETABLE_H
 +#define GMX_TABLES_FORCETABLE_H
 +
 +#include <cstdio>
 +
 +#include "gromacs/mdtypes/fcdata.h"
 +#include "gromacs/mdtypes/forcerec.h"
 +#include "gromacs/mdtypes/interaction_const.h"
 +#include "gromacs/utility/real.h"
 +
 +#define GMX_MAKETABLES_FORCEUSER  (1<<0)
 +#define GMX_MAKETABLES_14ONLY     (1<<1)
 +
 +/* Index in the tables that says which function to use */
 +enum {
 +    etiCOUL, etiLJ6, etiLJ12, etiNR
 +};
 +
 +typedef double (*real_space_grid_contribution_computer)(double, double);
 +/* Function pointer used to tell table_spline3_fill_ewald_lr whether it
 + * should calculate the grid contribution for electrostatics or LJ.
 + */
 +
 +void table_spline3_fill_ewald_lr(real                                 *table_F,
 +                                 real                                 *table_V,
 +                                 real                                 *table_FDV0,
 +                                 int                                   ntab,
 +                                 double                                dx,
 +                                 real                                  beta,
 +                                 real_space_grid_contribution_computer v_lr);
 +/* Fill tables of ntab points with spacing dr with the ewald long-range
 + * (mesh) force.
 + * There are three separate tables with format FDV0, F, and V.
 + * This function interpolates the Ewald mesh potential contribution
 + * with coefficient beta using a quadratic spline.
 + * The force can then be interpolated linearly.
 + */
 +
 +real ewald_spline3_table_scale(const interaction_const_t *ic);
 +/* Return the scaling for the Ewald quadratic spline tables. */
 +
 +double v_q_ewald_lr(double beta, double r);
 +/* Return the real space grid contribution for Ewald*/
 +
 +double v_lj_ewald_lr(double beta, double r);
 +/* Return the real space grid contribution for LJ-Ewald*/
 +
 +t_forcetable *make_tables(FILE *fp,
 +                          const t_forcerec *fr,
 +                          const char *fn, real rtab, int flags);
 +/* Return tables for inner loops. */
 +
++bondedtable_t make_bonded_table(FILE *fplog, const char *fn, int angle);
 +/* Return a table for bonded interactions,
 + * angle should be: bonds 0, angles 1, dihedrals 2
 + */
 +
 +/* Return a table for GB calculations */
 +t_forcetable *make_gb_table(const t_forcerec              *fr);
 +
 +/*! \brief Construct and return tabulated dispersion and repulsion interactions
 + *
 + * This table can be used to compute long-range dispersion corrections */
 +t_forcetable *makeDispersionCorrectionTable(FILE *fp, t_forcerec *fr,
 +                                            real rtab, const char *tabfn);
 +
 +#endif  /* GMX_TABLES_FORCETABLE_H */
index 864f58f8b9da447ce71a7503168368bd10fb74b4,b2b790e502c8afbde0c96056d3d084d6e588a33d..f54aedaa9ebf0b700b632e40e2c7f7ef04414bd2
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
-  * Copyright (c) 2011,2012,2013,2014,2015, by the GROMACS development team, led by
+  * Copyright (c) 2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
   * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
   * and including many others, as listed in the AUTHORS file in the
   * top-level source directory and at http://www.gromacs.org.
@@@ -59,7 -59,7 +59,7 @@@ namespace gm
   *
   * Does not throw.
   */
 -bool inline isNullOrEmpty(const char *str)
 +static inline bool isNullOrEmpty(const char *str)
  {
      return str == NULL || str[0] == '\0';
  }
   * Returns true if \p prefix is empty.
   * Does not throw.
   */
 -bool inline startsWith(const std::string &str, const std::string &prefix)
 +static inline bool startsWith(const std::string &str, const std::string &prefix)
  {
      return str.compare(0, prefix.length(), prefix) == 0;
  }
  //! \copydoc startsWith(const std::string &, const std::string &)
 -bool inline startsWith(const char *str, const char *prefix)
 +static inline bool startsWith(const char *str, const char *prefix)
  {
      return std::strncmp(str, prefix, std::strlen(prefix)) == 0;
  }
   * Returns true if \p suffix is NULL or empty.
   * Does not throw.
   */
 -bool endsWith(const std::string &str, const char *suffix);
 -
 -//! \copydoc endsWith(const std::string &str, const char *suffix)
 -static inline bool endsWith(const std::string &str, const std::string &suffix)
 +bool endsWith(const char *str, const char *suffix);
 +//! \copydoc endsWith(const char *, const char *)
 +static inline bool endsWith(const std::string &str, const char *suffix)
  {
 -    return endsWith(str, suffix.c_str());
 +    return endsWith(str.c_str(), suffix);
  }
  
  /*! \brief
@@@ -114,28 -115,12 +114,39 @@@ static inline bool contains(const std::
  {
      return str.find(substr) != std::string::npos;
  }
+ //! \copydoc contains(const std::string &str, const char *substr)
+ static inline bool contains(const std::string &str, const std::string &substr)
+ {
+     return str.find(substr) != std::string::npos;
+ }
  
 +/*!\brief Returns number of space-separated words in zero-terminated char ptr
 + *
 + * \param s Character pointer to zero-terminated, which will not be changed.
 + *
 + * \returns number of words in string.
 + *
 + * \note This routine is mainly meant to support legacy code in GROMACS. For
 + *       new source you should try hard to use C++ string objects instead.
 + */
 +std::size_t
 +countWords(const char *s);
 +
 +/*!\brief Returns the number of space-separated words in a string object
 + *
 + * \param str Reference to string object, which will not be changed.
 + *
 + * \returns number of words in string.
 + */
 +std::size_t
 +countWords(const std::string &str);
 +
++//! \copydoc endsWith(const std::string &str, const char *suffix)
++static inline bool endsWith(const std::string &str, const std::string &suffix)
++{
++    return endsWith(str, suffix.c_str());
++}
++
  /*! \brief
   * Removes a suffix from a string.
   *
@@@ -299,16 -284,6 +310,16 @@@ std::string joinStrings(const char *con
      return joinStrings(array, array + count, separator);
  }
  
 +/*! \brief
 + * Converts a boolean to a "true"/"false" string.
 + *
 + * Does not throw.
 + */
 +static inline const char *boolToString(bool value)
 +{
 +    return value ? "true" : "false";
 +}
 +
  /*! \brief
   * Splits a string to whitespace separated tokens.
   *
@@@ -385,7 -360,7 +396,7 @@@ class TextLineWrapperSetting
           *  - No maximum line width (only explicit line breaks).
           *  - No indentation.
           *  - No continuation characters.
 -         *  - Ignore whitespace after an explicit newline.
 +         *  - Do not keep final spaces in input strings.
           */
          TextLineWrapperSettings();
  
           */
          void setFirstLineIndent(int indent) { firstLineIndent_ = indent; }
          /*! \brief
 -         * Sets whether to remove spaces after an explicit newline.
 +         * Sets whether final spaces in input should be kept.
           *
 -         * \param[in] bStrip  If true, spaces after newline are ignored.
 +         * \param[in] bKeep  Whether to keep spaces at the end of the input.
           *
 -         * If not removed, the space is added to the indentation set with
 -         * setIndent().
 -         * The default is to not strip such whitespace.
 +         * This means that wrapping a string that ends in spaces also keeps
 +         * those spaces in the output.  This allows using the wrapper for
 +         * partial lines where the initial part of the line may end in a space.
 +         * By default, all trailing whitespace is removed.  Note that this
 +         * option does not affect spaces before an explicit newline: those are
 +         * always removed.
           */
 -        void setStripLeadingWhitespace(bool bStrip)
 -        {
 -            bStripLeadingWhitespace_ = bStrip;
 -        }
 +        void setKeepFinalSpaces(bool bKeep) { bKeepFinalSpaces_ = bKeep; }
          /*! \brief
           * Sets a continuation marker for wrapped lines.
           *
           * If -1, \a indent_ is used.
           */
          int                     firstLineIndent_;
 -        //! Whether to ignore or preserve space after a newline.
 -        bool                    bStripLeadingWhitespace_;
 +        //! Whether to keep spaces at end of input.
 +        bool                    bKeepFinalSpaces_;
          //! If not \c '\0', mark each wrapping point with this character.
          char                    continuationChar_;
  
@@@ -553,9 -528,6 +564,9 @@@ class TextLineWrappe
           */
          TextLineWrapperSettings &settings() { return settings_; }
  
 +        //! Returns true if the wrapper would not modify the input string.
 +        bool isTrivial() const;
 +
          /*! \brief
           * Finds the next line to be wrapped.
           *
index 9458dfbf91803983e33b4ae66f8661057fb2d4db,fcdc36952adcd0d2e1ad0dcf25c5c997e1a2dd72..e56c4a14530f5317f7679cb01a585b994a40d255
  #include <stdio.h>
  #include <string.h>
  
 +#include "gromacs/commandline/filenm.h"
  #include "gromacs/commandline/pargs.h"
 -#include "gromacs/fileio/filenm.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/main.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/readinp.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/fileio/readinp.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/mdlib/main.h"
 +#include "gromacs/mdlib/mdrun.h"
  #include "gromacs/mdrunutility/handlerestart.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/utility/arraysize.h"
  #include "gromacs/utility/fatalerror.h"
  
  #include "mdrun_main.h"
 +#include "runner.h"
  
  /*! \brief Return whether either of the command-line parameters that
   *  will trigger a multi-simulation is set */
@@@ -134,10 -134,11 +134,11 @@@ int gmx_mdrun(int argc, char *argv[]
          "functions is read using the [TT]-tablep[tt] option.[PAR]",
          "When tabulated bonded functions are present in the topology,",
          "interaction functions are read using the [TT]-tableb[tt] option.",
-         "For each different tabulated interaction type the table file name is",
-         "modified in a different way: before the file extension an underscore is",
-         "appended, then a 'b' for bonds, an 'a' for angles or a 'd' for dihedrals",
-         "and finally the table number of the interaction type.[PAR]",
+         "For each different tabulated interaction type used, a table file name must",
+         "be given. For the topology to work, a file name given here must match a",
+         "character sequence before the file extension. That sequence is: an underscore,",
+         "then a 'b' for bonds, an 'a' for angles or a 'd' for dihedrals,",
+         "and finally the matching table number index used in the topology.[PAR]",
          "The options [TT]-px[tt] and [TT]-pf[tt] are used for writing pull COM",
          "coordinates and forces when pulling is selected",
          "in the [REF].mdp[ref] file.[PAR]",
          "terminated only when the time limit set by [TT]-maxh[tt] is reached (if any)"
          "or upon receiving a signal."
          "[PAR]",
 -        "When [TT]mdrun[tt] receives a TERM signal, it will set nsteps to the current",
 -        "step plus one. When [TT]mdrun[tt] receives an INT signal (e.g. when ctrl+C is",
 -        "pressed), it will stop after the next neighbor search step ",
 -        "(with nstlist=0 at the next step).",
 +        "When [TT]mdrun[tt] receives a TERM signal, it will stop as soon as",
 +        "checkpoint file can be written, i.e. after the next global communication step.",
 +        "When [TT]mdrun[tt] receives an INT signal (e.g. when ctrl+C is",
 +        "pressed), it will stop at the next neighbor search step or at the",
 +        "second global communication step, whichever happens later.",
          "In both cases all the usual output will be written to file.",
          "When running with MPI, a signal to one of the [TT]mdrun[tt] ranks",
          "is sufficient, this signal should not be sent to mpirun or",
          "the [TT]mdrun[tt] process that is the parent of the others.",
          "[PAR]",
          "Interactive molecular dynamics (IMD) can be activated by using at least one",
 -        "of the three IMD switches: The [TT]-imdterm[tt] switch allows to terminate",
 +        "of the three IMD switches: The [TT]-imdterm[tt] switch allows one to terminate",
          "the simulation from the molecular viewer (e.g. VMD). With [TT]-imdwait[tt],",
          "[TT]mdrun[tt] pauses whenever no IMD client is connected. Pulling from the",
          "IMD remote can be turned on by [TT]-imdpull[tt].",
          { efXVG, "-dhdl",   "dhdl",     ffOPTWR },
          { efXVG, "-field",  "field",    ffOPTWR },
          { efXVG, "-table",  "table",    ffOPTRD },
 -        { efXVG, "-tabletf", "tabletf",    ffOPTRD },
          { efXVG, "-tablep", "tablep",   ffOPTRD },
-         { efXVG, "-tableb", "table",    ffOPTRD },
+         { efXVG, "-tableb", "table",    ffOPTRDMULT },
          { efTRX, "-rerun",  "rerun",    ffOPTRD },
          { efXVG, "-tpi",    "tpi",      ffOPTWR },
          { efXVG, "-tpid",   "tpidist",  ffOPTWR },
          { efLOG, "-rs",     "rotslabs", ffOPTWR },
          { efLOG, "-rt",     "rottorque", ffOPTWR },
          { efMTX, "-mtx",    "nm",       ffOPTWR },
 -        { efNDX, "-dn",     "dipole",   ffOPTWR },
          { efRND, "-multidir", NULL,      ffOPTRDMULT},
          { efDAT, "-membed", "membed",   ffOPTRD },
          { efTOP, "-mp",     "membed",   ffOPTRD },
      const int     NFILE = asize(fnm);
  
      /* Command line options ! */
 -    gmx_bool        bDDBondCheck  = TRUE;
 -    gmx_bool        bDDBondComm   = TRUE;
 -    gmx_bool        bTunePME      = TRUE;
 -    gmx_bool        bVerbose      = FALSE;
 -    gmx_bool        bCompact      = TRUE;
 -    gmx_bool        bRerunVSite   = FALSE;
 -    gmx_bool        bConfout      = TRUE;
 -    gmx_bool        bReproducible = FALSE;
 -    gmx_bool        bIMDwait      = FALSE;
 -    gmx_bool        bIMDterm      = FALSE;
 -    gmx_bool        bIMDpull      = FALSE;
 +    gmx_bool          bDDBondCheck  = TRUE;
 +    gmx_bool          bDDBondComm   = TRUE;
 +    gmx_bool          bTunePME      = TRUE;
 +    gmx_bool          bVerbose      = FALSE;
 +    gmx_bool          bRerunVSite   = FALSE;
 +    gmx_bool          bConfout      = TRUE;
 +    gmx_bool          bReproducible = FALSE;
 +    gmx_bool          bIMDwait      = FALSE;
 +    gmx_bool          bIMDterm      = FALSE;
 +    gmx_bool          bIMDpull      = FALSE;
  
 -    int             npme          = -1;
 -    int             nstlist       = 0;
 -    int             nmultisim     = 0;
 -    int             nstglobalcomm = -1;
 -    int             repl_ex_nst   = 0;
 -    int             repl_ex_seed  = -1;
 -    int             repl_ex_nex   = 0;
 -    int             nstepout      = 100;
 -    int             resetstep     = -1;
 -    gmx_int64_t     nsteps        = -2;   /* the value -2 means that the mdp option will be used */
 -    int             imdport       = 8888; /* can be almost anything, 8888 is easy to remember */
 +    int               npme          = -1;
 +    int               nstlist       = 0;
 +    int               nmultisim     = 0;
 +    int               nstglobalcomm = -1;
 +    int               repl_ex_nst   = 0;
 +    int               repl_ex_seed  = -1;
 +    int               repl_ex_nex   = 0;
 +    int               nstepout      = 100;
 +    int               resetstep     = -1;
 +    gmx_int64_t       nsteps        = -2;   /* the value -2 means that the mdp option will be used */
 +    int               imdport       = 8888; /* can be almost anything, 8888 is easy to remember */
  
 -    rvec            realddxyz          = {0, 0, 0};
 -    const char     *ddno_opt[ddnoNR+1] =
 +    rvec              realddxyz                   = {0, 0, 0};
 +    const char       *ddrank_opt[ddrankorderNR+1] =
      { NULL, "interleave", "pp_pme", "cartesian", NULL };
 -    const char     *dddlb_opt[] =
 +    const char       *dddlb_opt[] =
      { NULL, "auto", "no", "yes", NULL };
 -    const char     *thread_aff_opt[threadaffNR+1] =
 +    const char       *thread_aff_opt[threadaffNR+1] =
      { NULL, "auto", "on", "off", NULL };
 -    const char     *nbpu_opt[] =
 +    const char       *nbpu_opt[] =
      { NULL, "auto", "cpu", "gpu", "gpu_cpu", NULL };
 -    real            rdd                   = 0.0, rconstr = 0.0, dlb_scale = 0.8, pforce = -1;
 -    char           *ddcsx                 = NULL, *ddcsy = NULL, *ddcsz = NULL;
 -    real            cpt_period            = 15.0, max_hours = -1;
 -    gmx_bool        bTryToAppendFiles     = TRUE;
 -    gmx_bool        bKeepAndNumCPT        = FALSE;
 -    gmx_bool        bResetCountersHalfWay = FALSE;
 -    output_env_t    oenv                  = NULL;
 +    real              rdd                   = 0.0, rconstr = 0.0, dlb_scale = 0.8, pforce = -1;
 +    char             *ddcsx                 = NULL, *ddcsy = NULL, *ddcsz = NULL;
 +    real              cpt_period            = 15.0, max_hours = -1;
 +    gmx_bool          bTryToAppendFiles     = TRUE;
 +    gmx_bool          bKeepAndNumCPT        = FALSE;
 +    gmx_bool          bResetCountersHalfWay = FALSE;
 +    gmx_output_env_t *oenv                  = NULL;
  
      /* Non transparent initialization of a complex gmx_hw_opt_t struct.
       * But unfortunately we are not allowed to call a function here,
  
          { "-dd",      FALSE, etRVEC, {&realddxyz},
            "Domain decomposition grid, 0 is optimize" },
 -        { "-ddorder", FALSE, etENUM, {ddno_opt},
 +        { "-ddorder", FALSE, etENUM, {ddrank_opt},
            "DD rank order" },
          { "-npme",    FALSE, etINT, {&npme},
            "Number of separate ranks to be used for PME, -1 is guess" },
            "Optimize PME load between PP/PME ranks or GPU/CPU" },
          { "-v",       FALSE, etBOOL, {&bVerbose},
            "Be loud and noisy" },
 -        { "-compact", FALSE, etBOOL, {&bCompact},
 -          "Write a compact log file" },
          { "-pforce",  FALSE, etREAL, {&pforce},
            "Print all forces larger than this (kJ/mol nm)" },
          { "-reprod",  FALSE, etBOOL, {&bReproducible},
      };
      unsigned long   Flags;
      ivec            ddxyz;
 -    int             dd_node_order;
 +    int             dd_rank_order;
      gmx_bool        bDoAppendFiles, bStartFromCpt;
      FILE           *fplog;
      int             rc;
      }
  
  
 -    /* we set these early because they might be used in init_multisystem()
 -       Note that there is the potential for npme>nnodes until the number of
 -       threads is set later on, if there's thread parallelization. That shouldn't
 -       lead to problems. */
 -    dd_node_order = nenum(ddno_opt);
 -    cr->npmenodes = npme;
 +    dd_rank_order = nenum(ddrank_opt);
  
      hw_opt.thread_affinity = nenum(thread_aff_opt);
  
  
      if (nmultisim >= 1)
      {
 -#ifndef GMX_THREAD_MPI
 +#if !GMX_THREAD_MPI
          gmx_bool bParFn = (multidir == NULL);
          init_multisystem(cr, nmultisim, multidir, NFILE, fnm, bParFn);
  #else
      ddxyz[YY] = (int)(realddxyz[YY] + 0.5);
      ddxyz[ZZ] = (int)(realddxyz[ZZ] + 0.5);
  
 -    rc = mdrunner(&hw_opt, fplog, cr, NFILE, fnm, oenv, bVerbose, bCompact,
 -                  nstglobalcomm, ddxyz, dd_node_order, rdd, rconstr,
 -                  dddlb_opt[0], dlb_scale, ddcsx, ddcsy, ddcsz,
 -                  nbpu_opt[0], nstlist,
 -                  nsteps, nstepout, resetstep,
 -                  nmultisim, repl_ex_nst, repl_ex_nex, repl_ex_seed,
 -                  pforce, cpt_period, max_hours, imdport, Flags);
 +    rc = gmx::mdrunner(&hw_opt, fplog, cr, NFILE, fnm, oenv, bVerbose,
 +                       nstglobalcomm, ddxyz, dd_rank_order, npme, rdd, rconstr,
 +                       dddlb_opt[0], dlb_scale, ddcsx, ddcsy, ddcsz,
 +                       nbpu_opt[0], nstlist,
 +                       nsteps, nstepout, resetstep,
 +                       nmultisim, repl_ex_nst, repl_ex_nex, repl_ex_seed,
 +                       pforce, cpt_period, max_hours, imdport, Flags);
  
      /* Log file has to be closed in mdrunner if we are appending to it
         (fplog not set here) */
index 3294776ff121cf4b9ee740de07be997e6636fe2d,7489b07dcbee6c6f730415ef166ed857afaffe4b..969a3d9513503547578f12bcbfa06c9a893b6c40
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -
 +/*! \internal \file
 + *
 + * \brief Implements the MD runner routine calling all integrators.
 + *
 + * \author David van der Spoel <david.vanderspoel@icm.uu.se>
 + * \ingroup module_mdlib
 + */
  #include "gmxpre.h"
  
 +#include "runner.h"
 +
  #include "config.h"
  
  #include <assert.h>
  
  #include <algorithm>
  
 +#include "gromacs/commandline/filenm.h"
  #include "gromacs/domdec/domdec.h"
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/essentialdynamics/edsam.h"
  #include "gromacs/ewald/pme.h"
 +#include "gromacs/fileio/checkpoint.h"
 +#include "gromacs/fileio/oenv.h"
  #include "gromacs/fileio/tpxio.h"
 -#include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
 -#include "gromacs/legacyheaders/checkpoint.h"
 -#include "gromacs/legacyheaders/constr.h"
 -#include "gromacs/legacyheaders/copyrite.h"
 -#include "gromacs/legacyheaders/disre.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/gmx_detect_hardware.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/gmx_thread_affinity.h"
 -#include "gromacs/legacyheaders/inputrec.h"
 -#include "gromacs/legacyheaders/main.h"
 -#include "gromacs/legacyheaders/md_logging.h"
 -#include "gromacs/legacyheaders/md_support.h"
 -#include "gromacs/legacyheaders/mdatoms.h"
 -#include "gromacs/legacyheaders/mdrun.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/oenv.h"
 -#include "gromacs/legacyheaders/orires.h"
 -#include "gromacs/legacyheaders/qmmm.h"
 -#include "gromacs/legacyheaders/sighandler.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 +#include "gromacs/gmxlib/md_logging.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gpu_utils/gpu_utils.h"
 +#include "gromacs/hardware/cpuinfo.h"
 +#include "gromacs/hardware/detecthardware.h"
 +#include "gromacs/listed-forces/disre.h"
 +#include "gromacs/listed-forces/orires.h"
  #include "gromacs/math/calculate-ewald-splitting-coefficient.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/utilities.h"
  #include "gromacs/math/vec.h"
  #include "gromacs/mdlib/calc_verletbuf.h"
 +#include "gromacs/mdlib/constr.h"
 +#include "gromacs/mdlib/force.h"
 +#include "gromacs/mdlib/forcerec.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/integrator.h"
 +#include "gromacs/mdlib/main.h"
 +#include "gromacs/mdlib/md_support.h"
 +#include "gromacs/mdlib/mdatoms.h"
 +#include "gromacs/mdlib/mdrun.h"
 +#include "gromacs/mdlib/minimize.h"
  #include "gromacs/mdlib/nbnxn_search.h"
 +#include "gromacs/mdlib/qmmm.h"
 +#include "gromacs/mdlib/sighandler.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdlib/tpi.h"
 +#include "gromacs/mdrunutility/threadaffinity.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/state.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/pulling/pull.h"
  #include "gromacs/pulling/pull_rotation.h"
 -#include "gromacs/swap/swapcoords.h"
  #include "gromacs/timing/wallcycle.h"
  #include "gromacs/topology/mtop_util.h"
 +#include "gromacs/trajectory/trajectoryframe.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
  
  #include "deform.h"
 -#include "membed.h"
 +#include "md.h"
  #include "repl_ex.h"
  #include "resource-division.h"
  
  #include "corewrap.h"
  #endif
  
 -typedef struct {
 -    gmx_integrator_t *func;
 -} gmx_intp_t;
 -
 -/* The array should match the eI array in include/types/enums.h */
 -const gmx_intp_t    integrator[eiNR] = { {do_md}, {do_steep}, {do_cg}, {do_md}, {do_md}, {do_nm}, {do_lbfgs}, {do_tpi}, {do_tpi}, {do_md}, {do_md}, {do_md}};
 -
 +//! First step used in pressure scaling
  gmx_int64_t         deform_init_init_step_tpx;
 +//! Initial box for pressure scaling
  matrix              deform_init_box_tpx;
 +//! MPI variable for use in pressure scaling
  tMPI_Thread_mutex_t deform_init_box_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
  
 -
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
  /* The minimum number of atoms per tMPI thread. With fewer atoms than this,
   * the number of threads will get lowered.
   */
  
  struct mdrunner_arglist
  {
 -    gmx_hw_opt_t    hw_opt;
 -    FILE           *fplog;
 -    t_commrec      *cr;
 -    int             nfile;
 -    const t_filenm *fnm;
 -    output_env_t    oenv;
 -    gmx_bool        bVerbose;
 -    gmx_bool        bCompact;
 -    int             nstglobalcomm;
 -    ivec            ddxyz;
 -    int             dd_node_order;
 -    real            rdd;
 -    real            rconstr;
 -    const char     *dddlb_opt;
 -    real            dlb_scale;
 -    const char     *ddcsx;
 -    const char     *ddcsy;
 -    const char     *ddcsz;
 -    const char     *nbpu_opt;
 -    int             nstlist_cmdline;
 -    gmx_int64_t     nsteps_cmdline;
 -    int             nstepout;
 -    int             resetstep;
 -    int             nmultisim;
 -    int             repl_ex_nst;
 -    int             repl_ex_nex;
 -    int             repl_ex_seed;
 -    real            pforce;
 -    real            cpt_period;
 -    real            max_hours;
 -    int             imdport;
 -    unsigned long   Flags;
 +    gmx_hw_opt_t            hw_opt;
 +    FILE                   *fplog;
 +    t_commrec              *cr;
 +    int                     nfile;
 +    const t_filenm         *fnm;
 +    const gmx_output_env_t *oenv;
 +    gmx_bool                bVerbose;
 +    int                     nstglobalcomm;
 +    ivec                    ddxyz;
 +    int                     dd_rank_order;
 +    int                     npme;
 +    real                    rdd;
 +    real                    rconstr;
 +    const char             *dddlb_opt;
 +    real                    dlb_scale;
 +    const char             *ddcsx;
 +    const char             *ddcsy;
 +    const char             *ddcsz;
 +    const char             *nbpu_opt;
 +    int                     nstlist_cmdline;
 +    gmx_int64_t             nsteps_cmdline;
 +    int                     nstepout;
 +    int                     resetstep;
 +    int                     nmultisim;
 +    int                     repl_ex_nst;
 +    int                     repl_ex_nex;
 +    int                     repl_ex_seed;
 +    real                    pforce;
 +    real                    cpt_period;
 +    real                    max_hours;
 +    int                     imdport;
 +    unsigned long           Flags;
  };
  
  
     a commrec. */
  static void mdrunner_start_fn(void *arg)
  {
 -    struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg;
 -    struct mdrunner_arglist  mc  = *mda; /* copy the arg list to make sure
 -                                            that it's thread-local. This doesn't
 -                                            copy pointed-to items, of course,
 -                                            but those are all const. */
 -    t_commrec *cr;                       /* we need a local version of this */
 -    FILE      *fplog = NULL;
 -    t_filenm  *fnm;
 +    try
 +    {
 +        struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg;
 +        struct mdrunner_arglist  mc  = *mda; /* copy the arg list to make sure
 +                                                that it's thread-local. This doesn't
 +                                                copy pointed-to items, of course,
 +                                                but those are all const. */
 +        t_commrec *cr;                       /* we need a local version of this */
 +        FILE      *fplog = NULL;
 +        t_filenm  *fnm;
  
 -    fnm = dup_tfn(mc.nfile, mc.fnm);
 +        fnm = dup_tfn(mc.nfile, mc.fnm);
  
 -    cr = reinitialize_commrec_for_this_thread(mc.cr);
 +        cr = reinitialize_commrec_for_this_thread(mc.cr);
  
 -    if (MASTER(cr))
 -    {
 -        fplog = mc.fplog;
 -    }
 +        if (MASTER(cr))
 +        {
 +            fplog = mc.fplog;
 +        }
  
 -    mdrunner(&mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv,
 -             mc.bVerbose, mc.bCompact, mc.nstglobalcomm,
 -             mc.ddxyz, mc.dd_node_order, mc.rdd,
 -             mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
 -             mc.ddcsx, mc.ddcsy, mc.ddcsz,
 -             mc.nbpu_opt, mc.nstlist_cmdline,
 -             mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
 -             mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 -             mc.cpt_period, mc.max_hours, mc.imdport, mc.Flags);
 +        gmx::mdrunner(&mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv,
 +                      mc.bVerbose, mc.nstglobalcomm,
 +                      mc.ddxyz, mc.dd_rank_order, mc.npme, mc.rdd,
 +                      mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
 +                      mc.ddcsx, mc.ddcsy, mc.ddcsz,
 +                      mc.nbpu_opt, mc.nstlist_cmdline,
 +                      mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
 +                      mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 +                      mc.cpt_period, mc.max_hours, mc.imdport, mc.Flags);
 +    }
 +    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
  }
  
 +
  /* called by mdrunner() to start a specific number of threads (including
     the main thread) for thread-parallel runs. This in turn calls mdrunner()
     for each thread.
     All options besides nthreads are the same as for mdrunner(). */
  static t_commrec *mdrunner_start_threads(gmx_hw_opt_t *hw_opt,
                                           FILE *fplog, t_commrec *cr, int nfile,
 -                                         const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 -                                         gmx_bool bCompact, int nstglobalcomm,
 -                                         ivec ddxyz, int dd_node_order, real rdd, real rconstr,
 +                                         const t_filenm fnm[], const gmx_output_env_t *oenv, gmx_bool bVerbose,
 +                                         int nstglobalcomm,
 +                                         ivec ddxyz, int dd_rank_order, int npme,
 +                                         real rdd, real rconstr,
                                           const char *dddlb_opt, real dlb_scale,
                                           const char *ddcsx, const char *ddcsy, const char *ddcsz,
                                           const char *nbpu_opt, int nstlist_cmdline,
      mda->fnm             = fnmn;
      mda->oenv            = oenv;
      mda->bVerbose        = bVerbose;
 -    mda->bCompact        = bCompact;
      mda->nstglobalcomm   = nstglobalcomm;
      mda->ddxyz[XX]       = ddxyz[XX];
      mda->ddxyz[YY]       = ddxyz[YY];
      mda->ddxyz[ZZ]       = ddxyz[ZZ];
 -    mda->dd_node_order   = dd_node_order;
 +    mda->dd_rank_order   = dd_rank_order;
 +    mda->npme            = npme;
      mda->rdd             = rdd;
      mda->rconstr         = rconstr;
      mda->dddlb_opt       = dddlb_opt;
  #endif /* GMX_THREAD_MPI */
  
  
 -/* We determine the extra cost of the non-bonded kernels compared to
 +/*! \brief Cost of non-bonded kernels
 + *
 + * We determine the extra cost of the non-bonded kernels compared to
   * a reference nstlist value of 10 (which is the default in grompp).
   */
  static const int    nbnxnReferenceNstlist = 10;
 -/* The values to try when switching  */
 +//! The values to try when switching
  const int           nstlist_try[] = { 20, 25, 40 };
 +//! Number of elements in the neighborsearch list trials.
  #define NNSTL  sizeof(nstlist_try)/sizeof(nstlist_try[0])
  /* Increase nstlist until the non-bonded cost increases more than listfac_ok,
   * but never more than listfac_max.
   * nstlist will not be increased enough to reach optimal performance.
   */
  /* CPU: pair-search is about a factor 1.5 slower than the non-bonded kernel */
 +//! Max OK performance ratio beween force calc and neighbor searching
  static const float  nbnxn_cpu_listfac_ok    = 1.05;
 +//! Too high performance ratio beween force calc and neighbor searching
  static const float  nbnxn_cpu_listfac_max   = 1.09;
 +/* CPU: pair-search is about a factor 2-3 slower than the non-bonded kernel */
 +//! Max OK performance ratio beween force calc and neighbor searching
 +static const float  nbnxn_knl_listfac_ok    = 1.22;
 +//! Too high performance ratio beween force calc and neighbor searching
 +static const float  nbnxn_knl_listfac_max   = 1.3;
  /* GPU: pair-search is a factor 1.5-3 slower than the non-bonded kernel */
 +//! Max OK performance ratio beween force calc and neighbor searching
  static const float  nbnxn_gpu_listfac_ok    = 1.20;
 +//! Too high performance ratio beween force calc and neighbor searching
  static const float  nbnxn_gpu_listfac_max   = 1.30;
  
 -/* Try to increase nstlist when using the Verlet cut-off scheme */
 +/*! \brief Try to increase nstlist when using the Verlet cut-off scheme */
  static void increase_nstlist(FILE *fp, t_commrec *cr,
                               t_inputrec *ir, int nstlist_cmdline,
                               const gmx_mtop_t *mtop, matrix box,
 -                             gmx_bool bGPU)
 +                             gmx_bool bGPU, const gmx::CpuInfo &cpuinfo)
  {
      float                  listfac_ok, listfac_max;
      int                    nstlist_orig, nstlist_prev;
      const char            *box_err  = "Can not increase nstlist because the box is too small";
      const char            *dd_err   = "Can not increase nstlist because of domain decomposition limitations";
      char                   buf[STRLEN];
 -    const float            oneThird = 1.0f / 3.0f;
  
      if (nstlist_cmdline <= 0)
      {
          listfac_ok  = nbnxn_gpu_listfac_ok;
          listfac_max = nbnxn_gpu_listfac_max;
      }
 +    else if (cpuinfo.feature(gmx::CpuInfo::Feature::X86_Avx512ER))
 +    {
 +        listfac_ok  = nbnxn_knl_listfac_ok;
 +        listfac_max = nbnxn_knl_listfac_max;
 +    }
      else
      {
          listfac_ok  = nbnxn_cpu_listfac_ok;
      /* Determine the pair list size increase due to zero interactions */
      rlist_inc = nbnxn_get_rlist_effective_inc(ls.cluster_size_j,
                                                mtop->natoms/det(box));
 -    rlist_ok  = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_ok, oneThird) - rlist_inc;
 -    rlist_max = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_max, oneThird) - rlist_inc;
 +    rlist_ok  = (rlistWithReferenceNstlist + rlist_inc)*std::cbrt(listfac_ok) - rlist_inc;
 +    rlist_max = (rlistWithReferenceNstlist + rlist_inc)*std::cbrt(listfac_max) - rlist_inc;
      if (debug)
      {
          fprintf(debug, "nstlist tuning: rlist_inc %.3f rlist_ok %.3f rlist_max %.3f\n",
          calc_verlet_buffer_size(mtop, det(box), ir, -1, &ls, NULL, &rlist_new);
  
          /* Does rlist fit in the box? */
 -        bBox = (sqr(rlist_new) < max_cutoff2(ir->ePBC, box));
 +        bBox = (gmx::square(rlist_new) < max_cutoff2(ir->ePBC, box));
          bDD  = TRUE;
          if (bBox && DOMAINDECOMP(cr))
          {
              fprintf(fp, "%s\n\n", buf);
          }
          ir->rlist     = rlist_new;
 -        ir->rlistlong = rlist_new;
      }
  }
  
 +/*! \brief Initialize variables for Verlet scheme simulation */
  static void prepare_verlet_scheme(FILE                           *fplog,
                                    t_commrec                      *cr,
                                    t_inputrec                     *ir,
                                    int                             nstlist_cmdline,
                                    const gmx_mtop_t               *mtop,
                                    matrix                          box,
 -                                  gmx_bool                        bUseGPU)
 +                                  gmx_bool                        bUseGPU,
 +                                  const gmx::CpuInfo             &cpuinfo)
  {
      /* For NVE simulations, we will retain the initial list buffer */
      if (EI_DYNAMICS(ir->eI) &&
                          ls.cluster_size_i, ls.cluster_size_j);
              }
              ir->rlist     = rlist_new;
 -            ir->rlistlong = rlist_new;
          }
      }
  
      if (EI_DYNAMICS(ir->eI))
      {
          /* Set or try nstlist values */
 -        increase_nstlist(fplog, cr, ir, nstlist_cmdline, mtop, box, bUseGPU);
 +        increase_nstlist(fplog, cr, ir, nstlist_cmdline, mtop, box, bUseGPU, cpuinfo);
      }
  }
  
 -/* Override the value in inputrec with value passed on the command line (if any) */
 +/*! \brief Override the nslist value in inputrec
 + *
 + * with value passed on the command line (if any)
 + */
  static void override_nsteps_cmdline(FILE            *fplog,
                                      gmx_int64_t      nsteps_cmdline,
                                      t_inputrec      *ir,
      /* Do nothing if nsteps_cmdline == -2 */
  }
  
 +namespace gmx
 +{
 +
 +//! \brief Return the correct integrator function.
 +static integrator_t *my_integrator(unsigned int ei)
 +{
 +    switch (ei)
 +    {
 +        case eiMD:
 +        case eiBD:
 +        case eiSD1:
 +        case eiVV:
 +        case eiVVAK:
 +            if (!EI_DYNAMICS(ei))
 +            {
 +                GMX_THROW(APIError("do_md integrator would be called for a non-dynamical integrator"));
 +            }
 +            return do_md;
 +        case eiSteep:
 +            return do_steep;
 +        case eiCG:
 +            return do_cg;
 +        case eiNM:
 +            return do_nm;
 +        case eiLBFGS:
 +            return do_lbfgs;
 +        case eiTPI:
 +        case eiTPIC:
 +            if (!EI_TPI(ei))
 +            {
 +                GMX_THROW(APIError("do_tpi integrator would be called for a non-TPI integrator"));
 +            }
 +            return do_tpi;
 +        case eiSD2_REMOVED:
 +            GMX_THROW(NotImplementedError("SD2 integrator has been removed"));
 +        default:
 +            GMX_THROW(APIError("Non existing integrator selected"));
 +    }
 +}
 +
  int mdrunner(gmx_hw_opt_t *hw_opt,
               FILE *fplog, t_commrec *cr, int nfile,
 -             const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 -             gmx_bool bCompact, int nstglobalcomm,
 -             ivec ddxyz, int dd_node_order, real rdd, real rconstr,
 +             const t_filenm fnm[], const gmx_output_env_t *oenv, gmx_bool bVerbose,
 +             int nstglobalcomm,
 +             ivec ddxyz, int dd_rank_order, int npme, real rdd, real rconstr,
               const char *dddlb_opt, real dlb_scale,
               const char *ddcsx, const char *ddcsy, const char *ddcsz,
               const char *nbpu_opt, int nstlist_cmdline,
      gmx_constr_t              constr;
      int                       nChargePerturbed = -1, nTypePerturbed = 0, status;
      gmx_wallcycle_t           wcycle;
 -    gmx_bool                  bReadEkin;
      gmx_walltime_accounting_t walltime_accounting = NULL;
      int                       rc;
      gmx_int64_t               reset_counters;
      gmx_edsam_t               ed           = NULL;
      int                       nthreads_pme = 1;
 -    int                       nthreads_pp  = 1;
 -    gmx_membed_t              membed       = NULL;
      gmx_hw_info_t            *hwinfo       = NULL;
      /* The master rank decides early on bUseGPU and broadcasts this later */
 -    gmx_bool                  bUseGPU      = FALSE;
 +    gmx_bool                  bUseGPU            = FALSE;
  
      /* CAUTION: threads may be started later on in this function, so
         cr doesn't reflect the final parallel state right now */
      if (SIMMASTER(cr))
      {
          /* Read (nearly) all data required for the simulation */
 -        read_tpx_state(ftp2fn(efTPR, nfile, fnm), inputrec, state, NULL, mtop);
 +        read_tpx_state(ftp2fn(efTPR, nfile, fnm), inputrec, state, mtop);
  
          if (inputrec->cutoff_scheme == ecutsVERLET)
          {
  
              prepare_verlet_scheme(fplog, cr,
                                    inputrec, nstlist_cmdline, mtop, state->box,
 -                                  bUseGPU);
 +                                  bUseGPU, *hwinfo->cpuInfo);
          }
          else
          {
                  gmx_fatal(FARGS, "GPU requested, but can't be used without cutoff-scheme=Verlet");
              }
  
 -#ifdef GMX_TARGET_BGQ
 +#if GMX_TARGET_BGQ
              md_print_warn(cr, fplog,
                            "NOTE: There is no SIMD implementation of the group scheme kernels on\n"
                            "      BlueGene/Q. You will observe better performance from using the\n"
                            "      Verlet cut-off scheme.\n");
  #endif
          }
 -
 -        if (inputrec->eI == eiSD2)
 -        {
 -            md_print_warn(cr, fplog, "The stochastic dynamics integrator %s is deprecated, since\n"
 -                          "it is slower than integrator %s and is slightly less accurate\n"
 -                          "with constraints. Use the %s integrator.",
 -                          ei_names[inputrec->eI], ei_names[eiSD1], ei_names[eiSD1]);
 -        }
      }
  
      /* Check and update the hardware options for internal consistency */
 -    check_and_update_hw_opt_1(hw_opt, cr);
 +    check_and_update_hw_opt_1(hw_opt, cr, npme);
  
      /* Early check for externally set process affinity. */
      gmx_check_thread_affinity_set(fplog, cr,
                                    hw_opt, hwinfo->nthreads_hw_avail, FALSE);
  
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
      if (SIMMASTER(cr))
      {
 -        if (cr->npmenodes > 0 && hw_opt->nthreads_tmpi <= 0)
 +        if (npme > 0 && hw_opt->nthreads_tmpi <= 0)
          {
              gmx_fatal(FARGS, "You need to explicitly specify the number of MPI threads (-ntmpi) when using separate PME ranks");
          }
              t_commrec *cr_old       = cr;
              /* now start the threads. */
              cr = mdrunner_start_threads(hw_opt, fplog, cr_old, nfile, fnm,
 -                                        oenv, bVerbose, bCompact, nstglobalcomm,
 -                                        ddxyz, dd_node_order, rdd, rconstr,
 +                                        oenv, bVerbose, nstglobalcomm,
 +                                        ddxyz, dd_rank_order, npme, rdd, rconstr,
                                          dddlb_opt, dlb_scale, ddcsx, ddcsy, ddcsz,
                                          nbpu_opt, nstlist_cmdline,
                                          nsteps_cmdline, nstepout, resetstep, nmultisim,
  #endif
      /* END OF CAUTION: cr is now reliable */
  
 -    /* g_membed initialisation *
 -     * Because we change the mtop, init_membed is called before the init_parallel *
 -     * (in case we ever want to make it run in parallel) */
 -    if (opt2bSet("-membed", nfile, fnm))
 -    {
 -        if (MASTER(cr))
 -        {
 -            fprintf(stderr, "Initializing membed");
 -        }
 -        membed = init_membed(fplog, nfile, fnm, mtop, inputrec, state, cr, &cpt_period);
 -    }
 -
      if (PAR(cr))
      {
          /* now broadcast everything to the non-master nodes/threads: */
      /* A parallel command line option consistency check that we can
         only do after any threads have started. */
      if (!PAR(cr) &&
 -        (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || cr->npmenodes > 0))
 +        (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || npme > 0))
      {
          gmx_fatal(FARGS,
                    "The -dd or -npme option request a parallel simulation, "
 -#ifndef GMX_MPI
 +#if !GMX_MPI
                    "but %s was compiled without threads or MPI enabled"
  #else
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
                    "but the number of MPI-threads (option -ntmpi) is not set or is 1"
  #else
                    "but %s was not started through mpirun/mpiexec or only one rank was requested through mpirun/mpiexec"
  
      if (!(EEL_PME(inputrec->coulombtype) || EVDW_PME(inputrec->vdwtype)))
      {
 -        if (cr->npmenodes > 0)
 +        if (npme > 0)
          {
 -            gmx_fatal_collective(FARGS, cr, NULL,
 +            gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
                                   "PME-only ranks are requested, but the system does not use PME for electrostatics or LJ");
          }
  
 -        cr->npmenodes = 0;
 +        npme = 0;
      }
  
 -    if (bUseGPU && cr->npmenodes < 0)
 +    if (bUseGPU && npme < 0)
      {
          /* With GPUs we don't automatically use PME-only ranks. PME ranks can
           * improve performance with many threads per GPU, since our OpenMP
           * scaling is bad, but it's difficult to automate the setup.
           */
 -        cr->npmenodes = 0;
 +        npme = 0;
      }
  
  #ifdef GMX_FAHCORE
      init_orires(fplog, mtop, state->x, inputrec, cr, &(fcd->orires),
                  state);
  
 -    if (DEFORM(*inputrec))
 +    if (inputrecDeform(inputrec))
      {
          /* Store the deform reference box before reading the checkpoint */
          if (SIMMASTER(cr))
          tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
      }
  
 -    if (opt2bSet("-cpi", nfile, fnm))
 +    if (Flags & MD_STARTFROMCPT)
      {
          /* Check if checkpoint file exists before doing continuation.
           * This way we can use identical input options for the first and subsequent runs...
           */
 -        if (gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr) )
 -        {
 -            load_checkpoint(opt2fn_master("-cpi", nfile, fnm, cr), &fplog,
 -                            cr, ddxyz,
 -                            inputrec, state, &bReadEkin,
 -                            (Flags & MD_APPENDFILES),
 -                            (Flags & MD_APPENDFILESSET));
 +        gmx_bool bReadEkin;
  
 -            if (bReadEkin)
 -            {
 -                Flags |= MD_READ_EKIN;
 -            }
 +        load_checkpoint(opt2fn_master("-cpi", nfile, fnm, cr), &fplog,
 +                        cr, ddxyz, &npme,
 +                        inputrec, state, &bReadEkin,
 +                        (Flags & MD_APPENDFILES),
 +                        (Flags & MD_APPENDFILESSET));
 +
 +        if (bReadEkin)
 +        {
 +            Flags |= MD_READ_EKIN;
          }
      }
  
          gmx_bcast(sizeof(box), box, cr);
      }
  
 +    // TODO This should move to do_md(), because it only makes sense
 +    // with dynamical integrators, but there is no test coverage and
 +    // it interacts with constraints, somehow.
      /* Essential dynamics */
      if (opt2bSet("-ei", nfile, fnm))
      {
      if (PAR(cr) && !(EI_TPI(inputrec->eI) ||
                       inputrec->eI == eiNM))
      {
 -        cr->dd = init_domain_decomposition(fplog, cr, Flags, ddxyz, rdd, rconstr,
 +        cr->dd = init_domain_decomposition(fplog, cr, Flags, ddxyz, npme,
 +                                           dd_rank_order,
 +                                           rdd, rconstr,
                                             dddlb_opt, dlb_scale,
                                             ddcsx, ddcsy, ddcsz,
                                             mtop, inputrec,
                                             box, state->x,
                                             &ddbox, &npme_major, &npme_minor);
 -
 -        make_dd_communicators(fplog, cr, dd_node_order);
 -
 -        /* Set overallocation to avoid frequent reallocation of arrays */
 -        set_over_alloc_dd(TRUE);
      }
      else
      {
  
      /* Initialize per-physical-node MPI process/thread ID and counters. */
      gmx_init_intranode_counters(cr);
 -#ifdef GMX_MPI
 +#if GMX_MPI
      if (MULTISIM(cr))
      {
          md_print_info(cr, fplog,
      }
      md_print_info(cr, fplog, "Using %d MPI %s\n",
                    cr->nnodes,
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
                    cr->nnodes == 1 ? "thread" : "threads"
  #else
                    cr->nnodes == 1 ? "process" : "processes"
                            inputrec->cutoff_scheme == ecutsVERLET);
  
  #ifndef NDEBUG
 -    if (integrator[inputrec->eI].func != do_tpi &&
 +    if (EI_TPI(inputrec->eI) &&
          inputrec->cutoff_scheme == ecutsVERLET)
      {
          gmx_feenableexcept();
         PME: env variable should be read only on one node to make sure it is
         identical everywhere;
       */
 -    /* TODO nthreads_pp is only used for pinning threads.
 -     * This is a temporary solution until we have a hw topology library.
 -     */
 -    nthreads_pp  = gmx_omp_nthreads_get(emntNonbonded);
      nthreads_pme = gmx_omp_nthreads_get(emntPME);
  
 -    wcycle = wallcycle_init(fplog, resetstep, cr, nthreads_pp, nthreads_pme);
 +    wcycle = wallcycle_init(fplog, resetstep, cr);
  
      if (PAR(cr))
      {
          fr          = mk_forcerec();
          fr->hwinfo  = hwinfo;
          fr->gpu_opt = &hw_opt->gpu_opt;
 -        init_forcerec(fplog, oenv, fr, fcd, inputrec, mtop, cr, box,
 +        init_forcerec(fplog, fr, fcd, inputrec, mtop, cr, box,
                        opt2fn("-table", nfile, fnm),
 -                      opt2fn("-tabletf", nfile, fnm),
                        opt2fn("-tablep", nfile, fnm),
-                       opt2fn("-tableb", nfile, fnm),
+                       getFilenm("-tableb", nfile, fnm),
                        nbpu_opt,
                        FALSE,
                        pforce);
  
 -        /* version for PCA_NOT_READ_NODE (see md.c) */
 -        /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
 -           "nofile","nofile","nofile","nofile",FALSE,pforce);
 -         */
 -
          /* Initialize QM-MM */
          if (fr->bQMMM)
          {
      }
  
  
 -    if (integrator[inputrec->eI].func == do_md)
 +    if (EI_DYNAMICS(inputrec->eI))
      {
          /* Turn on signal handling on all nodes */
          /*
          if (inputrec->bRot)
          {
              /* Initialize enforced rotation code */
 -            init_rot(fplog, inputrec, nfile, fnm, cr, state->x, box, mtop, oenv,
 +            init_rot(fplog, inputrec, nfile, fnm, cr, state->x, state->box, mtop, oenv,
                       bVerbose, Flags);
          }
  
 -        if (inputrec->eSwapCoords != eswapNO)
 -        {
 -            /* Initialize ion swapping code */
 -            init_swapcoords(fplog, bVerbose, inputrec, opt2fn_master("-swap", nfile, fnm, cr),
 -                            mtop, state->x, state->box, &state->swapstate, cr, oenv, Flags);
 -        }
 -
          constr = init_constraints(fplog, mtop, inputrec, ed, state, cr);
  
          if (DOMAINDECOMP(cr))
          {
              GMX_RELEASE_ASSERT(fr, "fr was NULL while cr->duty was DUTY_PP");
 +            /* This call is not included in init_domain_decomposition mainly
 +             * because fr->cginfo_mb is set later.
 +             */
              dd_init_bondeds(fplog, cr->dd, mtop, vsite, inputrec,
                              Flags & MD_DDBONDCHECK, fr->cginfo_mb);
 -
 -            set_dd_parameters(fplog, cr->dd, dlb_scale, inputrec, &ddbox);
 -
 -            setup_dd_grid(fplog, cr->dd);
          }
  
          /* Now do whatever the user wants us to do (how flexible...) */
 -        integrator[inputrec->eI].func(fplog, cr, nfile, fnm,
 -                                      oenv, bVerbose, bCompact,
 -                                      nstglobalcomm,
 -                                      vsite, constr,
 -                                      nstepout, inputrec, mtop,
 -                                      fcd, state,
 -                                      mdatoms, nrnb, wcycle, ed, fr,
 -                                      repl_ex_nst, repl_ex_nex, repl_ex_seed,
 -                                      membed,
 -                                      cpt_period, max_hours,
 -                                      imdport,
 -                                      Flags,
 -                                      walltime_accounting);
 +        my_integrator(inputrec->eI) (fplog, cr, nfile, fnm,
 +                                     oenv, bVerbose,
 +                                     nstglobalcomm,
 +                                     vsite, constr,
 +                                     nstepout, inputrec, mtop,
 +                                     fcd, state,
 +                                     mdatoms, nrnb, wcycle, ed, fr,
 +                                     repl_ex_nst, repl_ex_nex, repl_ex_seed,
 +                                     cpt_period, max_hours,
 +                                     imdport,
 +                                     Flags,
 +                                     walltime_accounting);
  
 -        if (inputrec->bPull)
 +        if (inputrec->bRot)
          {
 -            finish_pull(inputrec->pull_work);
 +            finish_rot(inputrec->rot);
          }
  
 -        if (inputrec->bRot)
 +        if (inputrec->bPull)
          {
 -            finish_rot(inputrec->rot);
 +            finish_pull(inputrec->pull_work);
          }
  
      }
      /* Free GPU memory and context */
      free_gpu_resources(fr, cr, &hwinfo->gpu_info, fr ? fr->gpu_opt : NULL);
  
 -    if (opt2bSet("-membed", nfile, fnm))
 -    {
 -        sfree(membed);
 -    }
 -
      gmx_hardware_info_free(hwinfo);
  
      /* Does what it says */
  
      done_ed(&ed);
  
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
      /* we need to join all threads. The sub-threads join when they
         exit this function, but the master thread needs to be told to
         wait for that. */
  
      return rc;
  }
 +
 +} // namespace gmx
index c84a5d074ddb8fbed69aafbf167dadb94b42e6e2,22dc2532fc86f5bf7a036a6507117c733ce8fe91..cfbdb09568d3d5df5f3fae98d8c62d5026b7eafe
  set(testname "MdrunTests")
  set(exename "mdrun-test")
  
 -gmx_build_unit_test(
 -    ${testname}
 +gmx_add_gtest_executable(
      ${exename}
      # files with code for tests
+     tabulated_bonded_interactions.cpp
 +    energyreader.cpp
      grompp.cpp
      rerun.cpp
      trajectory_writing.cpp
 +    trajectoryreader.cpp
      compressed_x_output.cpp
      swapcoords.cpp
      interactiveMD.cpp
      # files with code for test fixtures
 +    mdruncomparisonfixture.cpp
      moduletest.cpp
      # pseudo-library for code for mdrun
      $<TARGET_OBJECTS:mdrun_objlib>
@@@ -60,8 -59,9 +61,8 @@@ gmx_register_integration_test
  set(testname "MdrunMpiTests")
  set(exename "mdrun-mpi-test")
  
 -gmx_build_unit_test(
 -    ${testname}
 -    ${exename}
 +gmx_add_gtest_executable(
 +    ${exename} MPI
      # files with code for tests
      multisim.cpp
      multisimtest.cpp
index 0000000000000000000000000000000000000000,9a7f3315076886262f353cf91ff0ce7b71053671..4e18b667f8f470bff7bbec7aaac50aa0bc9de5b8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,209 +1,209 @@@
 -#include "gromacs/utility/file.h"
+ /*
+  * This file is part of the GROMACS molecular simulation package.
+  *
+  * Copyright (c) 2016, by the GROMACS development team, led by
+  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+  * and including many others, as listed in the AUTHORS file in the
+  * top-level source directory and at http://www.gromacs.org.
+  *
+  * GROMACS is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public License
+  * as published by the Free Software Foundation; either version 2.1
+  * of the License, or (at your option) any later version.
+  *
+  * GROMACS is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * Lesser General Public License for more details.
+  *
+  * You should have received a copy of the GNU Lesser General Public
+  * License along with GROMACS; if not, see
+  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+  *
+  * If you want to redistribute modifications to GROMACS, please
+  * consider that scientific software is very special. Version
+  * control is crucial - bugs must be traceable. We will be happy to
+  * consider code for inclusion in the official distribution, but
+  * derived work must not be called official GROMACS. Details are found
+  * in the README & COPYING files - if they are missing, get the
+  * official version at http://www.gromacs.org.
+  *
+  * To help us fund GROMACS development, we humbly ask that you cite
+  * the research papers on the package. Check out http://www.gromacs.org.
+  */
+ /*! \internal \file
+  * \brief
+  * Tests for tabulated bonded interactions
+  *
+  * \author Mark Abraham <mark.j.abraham@gmail.com>
+  * \ingroup module_mdrun_integration_tests
+  */
+ #include "gmxpre.h"
+ #include <gtest/gtest.h>
 -            File::writeFileFromString(runner_.topFileName_, formatString(g_butaneTopFileFormatString, interaction));
+ #include "gromacs/utility/stringutil.h"
++#include "gromacs/utility/textwriter.h"
+ #include "testutils/cmdlinetest.h"
+ #include "moduletest.h"
+ namespace gmx
+ {
+ namespace test
+ {
+ //! Format string for building a configurable .top file
+ const char *g_butaneTopFileFormatString = "\
+ [ defaults ]\n\
+ ; nbfunc      comb-rule       gen-pairs       fudgeLJ fudgeQQ\n\
+   1           1               no              1.0     1.0\n\
+ \n\
+ [ atomtypes ]\n\
+ ;name        mass      charge   ptype            c6           c12\n\
+   CH2    14.02700       0.000       A   0.90975E-02   0.35333E-04\n\
+   CH3    15.03500       0.000       A   0.88765E-02   0.26150E-04\n\
+ \n\
+ [ moleculetype ]\n\
+ ;             name    nrexcl\n\
+             butane   3\n\
+ \n\
+ [ atoms ]\n\
+ ;   nr    type   resnr  residu    atom    cgnr\n\
+      1     CH3       1     BUT      C1       1\n\
+      2     CH2       1     BUT      C2       2\n\
+      3     CH2       1     BUT      C3       3\n\
+      4     CH3       1     BUT      C4       4\n\
+ \n\
+ %s\n\
+ \n\
+ [ system ]\n\
+ ; The name of the system to be simulated\n\
+ A single butane\n\
+ \n\
+ [ molecules ]\n\
+ ; Molname             Number\n\
+ Butane                   1\n\
+ ";
+ //! Test fixture for bonded interactions
+ class BondedInteractionsTest : public gmx::test::MdrunTestFixture
+ {
+     public:
+         //! Execute the trajectory writing test
+         void setupGrompp(const char *interaction)
+         {
+             runner_.topFileName_ = fileManager_.getTemporaryFilePath("butane1.top");
++            TextWriter::writeFileFromString(runner_.topFileName_, formatString(g_butaneTopFileFormatString, interaction));
+             runner_.groFileName_ = fileManager_.getInputFilePath("butane1.gro");
+             runner_.ndxFileName_ = fileManager_.getInputFilePath("butane1.ndx");
+             /* TODO Now that Verlet is the default, change the implementation
+                of useEmptyMdpFile() to do that. */
+             runner_.useStringAsMdpFile("");
+         }
+         //! Prepare an mdrun caller
+         CommandLine setupMdrun()
+         {
+             CommandLine rerunCaller;
+             rerunCaller.append("mdrun");
+             rerunCaller.addOption("-rerun", runner_.groFileName_);
+             return rerunCaller;
+         }
+         //! Check the output of mdrun
+         void checkMdrun()
+         {
+             // TODO verifying some energies and forces would be good,
+             // once other code in gerrit is reviewed
+         }
+ };
+ // This test ensures that a normal non-tabulated bond interaction works
+ TEST_F(BondedInteractionsTest, NormalBondWorks)
+ {
+     setupGrompp("[ bonds ]\n\
+ ;  ai    aj funct           c0           c1\n\
+     1     2     1 1.530000e-01 3.347000e+05");
+     EXPECT_EQ(0, runner_.callGrompp());
+     test::CommandLine rerunCaller = setupMdrun();
+     ASSERT_EQ(0, runner_.callMdrun(rerunCaller));
+     checkMdrun();
+ }
+ // This test ensures that a normal abulated bond interaction works
+ TEST_F(BondedInteractionsTest, TabulatedBondWorks)
+ {
+     setupGrompp("[ bonds ]\n\
+ ;  ai    aj funct  n     k\n\
+     1     2     8  0  1000");
+     EXPECT_EQ(0, runner_.callGrompp());
+     test::CommandLine rerunCaller   = setupMdrun();
+     std::string       tableFileName = fileManager_.getInputFilePath("butane_b0.xvg");
+     rerunCaller.addOption("-tableb", tableFileName);
+     ASSERT_EQ(0, runner_.callMdrun(rerunCaller));
+     checkMdrun();
+ }
+ // This test ensures that a normal non-tabulated angle interaction works
+ TEST_F(BondedInteractionsTest, NormalAngleWorks)
+ {
+     setupGrompp("[ angles ]\n\
+ ;  ai    aj    ak funct           c0           c1\n\
+     1     2     3     1 1.110000e+02 4.602000e+02");
+     EXPECT_EQ(0, runner_.callGrompp());
+     test::CommandLine rerunCaller = setupMdrun();
+     ASSERT_EQ(0, runner_.callMdrun(rerunCaller));
+     checkMdrun();
+ }
+ // This test ensures that a tabulated angle interaction works
+ TEST_F(BondedInteractionsTest, TabulatedAngleWorks)
+ {
+     setupGrompp("[ angles ]\n\
+ ;  ai    aj    ak funct  n     k\n\
+     1     2     3     8  0  1000");
+     EXPECT_EQ(0, runner_.callGrompp());
+     test::CommandLine rerunCaller   = setupMdrun();
+     std::string       tableFileName = fileManager_.getInputFilePath("butane_a0.xvg");
+     rerunCaller.addOption("-tableb", tableFileName);
+     ASSERT_EQ(0, runner_.callMdrun(rerunCaller));
+     checkMdrun();
+ }
+ // This test ensures that a normal non-tabulated dihedral interaction works
+ TEST_F(BondedInteractionsTest, NormalDihedralWorks)
+ {
+     setupGrompp("[ dihedrals ]\n \
+ ;  ai    aj    ak    al funct     c0     c1     c2      c3     c4      c5\n\
+     1     2     3     4     3 9.2789 12.156 -13.12 -3.0597  26.24 -31.495");
+     EXPECT_EQ(0, runner_.callGrompp());
+     test::CommandLine rerunCaller = setupMdrun();
+     ASSERT_EQ(0, runner_.callMdrun(rerunCaller));
+     checkMdrun();
+ }
+ // This test ensures that a tabulated dihedral interaction works
+ TEST_F(BondedInteractionsTest, TabulatedDihedralWorks)
+ {
+     setupGrompp("[ dihedrals ]\n\
+ ;  ai    aj    ak    al funct   n     k\n\
+     1     2     3     4     8   0  1000");
+     EXPECT_EQ(0, runner_.callGrompp());
+     test::CommandLine rerunCaller   = setupMdrun();
+     std::string       tableFileName = fileManager_.getInputFilePath("butane_d0.xvg");
+     rerunCaller.addOption("-tableb", tableFileName);
+     ASSERT_EQ(0, runner_.callMdrun(rerunCaller));
+     checkMdrun();
+ }
+ } // namespace
+ } // namespace