Merge release-5-0 into master
authorRoland Schulz <roland@utk.edu>
Sat, 16 Aug 2014 04:30:10 +0000 (00:30 -0400)
committerRoland Schulz <roland@utk.edu>
Sat, 16 Aug 2014 21:20:50 +0000 (17:20 -0400)
Conflicts (trivial):
src/gromacs/commandline/pargs.cpp
src/gromacs/gmxlib/gmx_thread_affinity.c
src/gromacs/options/filenameoption.cpp

Manual changes required for ConstArrayRef:
       src/gromacs/commandline/pargs.cpp
       src/gromacs/options/filenameoption.cpp
       src/gromacs/selection/selection.cpp
       src/gromacs/selection/tests/nbsearch.cpp
Manual moved REGRESSIONTEST_MD5SUM from
       CMakeLists.txt to cmake/gmxVersionInfo.cmake

Change-Id: I1a2038f72c1e1cfed830de5c540ab278dbb39cfe

22 files changed:
1  2 
CMakeLists.txt
cmake/gmxVersionInfo.cmake
install-guide/install-guide.md
src/config.h.cmakein
src/gromacs/commandline/pargs.cpp
src/gromacs/gmxlib/gmx_cpuid.c
src/gromacs/gmxlib/gmx_thread_affinity.c
src/gromacs/gmxpreprocess/readir.c
src/gromacs/legacyheaders/pme.h
src/gromacs/mdlib/domdec.c
src/gromacs/mdlib/perf_est.c
src/gromacs/mdlib/pme.c
src/gromacs/mdlib/pme_pp.c
src/gromacs/mdlib/tpi.c
src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/options/filenameoption.cpp
src/gromacs/selection/nbsearch.cpp
src/gromacs/selection/selection.cpp
src/gromacs/selection/selection.h
src/gromacs/selection/tests/nbsearch.cpp
src/gromacs/simd/tests/simd_math.cpp
tests/CMakeLists.txt

diff --combined CMakeLists.txt
index 964612afad160d0b4c0863ce814048f85c594544,246920662f930d46748a52914e9a1251f6b40295..bc12271f47fabc2a150b4179f14c0ab3f835d90b
@@@ -48,28 -48,75 +48,28 @@@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CM
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
  
 -# PROJECT_VERSION should have the following structure:
 -# VERSION-dev[-SUFFIX] where the VERSION should have the for: vMajor.vMinor.vPatch
 -#
 -# The "-dev" suffix is important to keep because it makes possible to distinguish
 -# between a build from official release and a build from git release branch on a
 -# machine with no git.
 -#
 -# NOTE: when releasing the "-dev" suffix needs to be stripped off!
 -# REGRESSIONTEST_VERSION and REGRESSIONTEST_BRANCH should always be
 -# defined.
 -set(PROJECT_VERSION "5.0.1-dev")
 -# If this is a released tarball, "-dev" will not be present in
 -# PROJECT_VERSION, and REGRESSIONTEST_VERSION specifies the version
 -# number of the regressiontest tarball against which the code tarball
 -# can be tested. This will be the version of the last patch release.
 -set(REGRESSIONTEST_VERSION "5.0.1-dev")
 -# The MD5 checksum of the regressiontest tarball. Only used if "-dev"
 -# is not present in the PROJECT_VERSION
 -set(REGRESSIONTEST_MD5SUM "a07524afebca5013540d4f2f72df2dce")
 -# If this is not a released tarball, "-dev" will be present in
 -# PROJECT_VERSION, and REGRESSIONTEST_BRANCH specifies the name of the
 -# gerrit.gromacs.org branch whose HEAD can test this code, *if* this
 -# code contains all recent fixes from the corresponding code branch.
 -set(REGRESSIONTEST_BRANCH "refs/heads/release-5-0")
 -
 -set(CUSTOM_VERSION_STRING ""
 -    CACHE STRING "Custom version string (if empty, use hard-coded default)")
 -mark_as_advanced(CUSTOM_VERSION_STRING)
 -if (CUSTOM_VERSION_STRING)
 -    set(PROJECT_VERSION ${CUSTOM_VERSION_STRING})
 -endif()
 -set(LIBRARY_SOVERSION 0)
 -set(LIBRARY_VERSION ${LIBRARY_SOVERSION}.0.0)
 -# It is a bit irritating, but this has to be set separately for now!
 -SET(CPACK_PACKAGE_VERSION_MAJOR "5")
 -SET(CPACK_PACKAGE_VERSION_MINOR "0")
 -#SET(CPACK_PACKAGE_VERSION_PATCH "0")
 -
 -# The numerical gromacs version. It is 40600 for 4.6.0.
 -# The #define GMX_VERSION in gromacs/version.h is set to this value.
 -math(EXPR NUM_VERSION
 -    "${CPACK_PACKAGE_VERSION_MAJOR}*10000 + ${CPACK_PACKAGE_VERSION_MINOR}*100")
 -if(CPACK_PACKAGE_VERSION_PATCH)
 -    math(EXPR NUM_VERSION
 -         "${NUM_VERSION} + ${CPACK_PACKAGE_VERSION_PATCH}")
 -endif()
 -
 -# The API version tracks the numerical Gromacs version (for now).
 -# It is potentially different from the Gromacs version in the future, if
 -# the programs/libraries diverge from the presumably more stable API.
 -# The #define GMX_API_VERSION in version.h is set to this value to
 -# provide backward compatibility of software written against the Gromacs API.
 -set(API_VERSION ${NUM_VERSION})
 +# Set up common version variables, as well as general information about
 +# 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(gmxVersionInfo)
  
  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND UNIX)
      set(CMAKE_INSTALL_PREFIX "/usr/local/gromacs" CACHE STRING "Installation prefix (installation will need write permissions here)" FORCE)
  endif()
  
  include(gmxBuildTypeReference)
 +include(gmxBuildTypeProfile)
  include(gmxBuildTypeTSAN)
  include(gmxBuildTypeASAN)
  include(gmxBuildTypeReleaseWithAssert)
  
  if(NOT CMAKE_BUILD_TYPE)
 -    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert." FORCE)
 +    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert Profile." FORCE)
      # There's no need to offer a user the choice of ThreadSanitizer
      # Set the possible values of build type for cmake-gui
      set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
 -        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert")
 +        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert" "Profile")
  endif()
  if(CMAKE_CONFIGURATION_TYPES)
      # Add appropriate GROMACS-specific build types for the Visual
          "List of configuration types"
          FORCE)
  endif()
 -set(build_types_with_explicit_flags RELEASE DEBUG RELWITHDEBUGINFO RELWITHASSERT MINSIZEREL)
 -
 -enable_language(C)
 -enable_language(CXX)
 +set(build_types_with_explicit_flags RELEASE DEBUG RELWITHDEBUGINFO RELWITHASSERT MINSIZEREL PROFILE)
  
  set(CPACK_PACKAGE_NAME "gromacs")
 -set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
 +set(CPACK_PACKAGE_VERSION_MAJOR ${GMX_VERSION_MAJOR})
 +set(CPACK_PACKAGE_VERSION_MINOR ${GMX_VERSION_MINOR})
 +set(CPACK_PACKAGE_VERSION_PATCH ${GMX_VERSION_PATCH})
 +set(CPACK_PACKAGE_VERSION       ${GMX_VERSION_STRING})
  set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
  set(CPACK_PACKAGE_VENDOR "gromacs.org")
  set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Gromacs - a toolkit for high-performance molecular simulation")
@@@ -117,9 -164,14 +117,9 @@@ set(MEMORYCHECK_SUPPRESSIONS_FIL
      "File that contains suppressions for the memory checker")
  include(CTest)
  
 -set(SOURCE_IS_GIT_REPOSITORY OFF)
 -set(SOURCE_IS_SOURCE_DISTRIBUTION OFF)
 -if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
 -    set(SOURCE_IS_GIT_REPOSITORY ON)
 -endif()
 -if(NOT EXISTS "${CMAKE_SOURCE_DIR}/admin/.isreposource")
 -    set(SOURCE_IS_SOURCE_DISTRIBUTION ON)
 -endif()
 +# Variables that accumulate stuff influencing the installed headers
 +set(INSTALLED_HEADER_INCLUDE_DIRS "")
 +set(INSTALLED_HEADER_DEFINITIONS "")
  
  ########################################################################
  # Check and warn if cache generated on a different host is being reused
@@@ -239,6 -291,9 +239,6 @@@ option(GMX_OPENMP "Enable OpenMP-based 
  
  option(GMX_USE_TNG "Use the TNG library for trajectory I/O" ON)
  
 -option(GMX_GIT_VERSION_INFO "Generate git version information" ${SOURCE_IS_GIT_REPOSITORY})
 -mark_as_advanced(GMX_GIT_VERSION_INFO)
 -
  if(UNIX)
      option(GMX_SYMLINK_OLD_BINARY_NAMES "Create symbolic links for pre-5.0 binary names" ON)
  endif()
@@@ -270,14 -325,20 +270,20 @@@ include(gmxManageOpenMP
  #####################################################################
  
  # The cmake/Check{C,CXX}CompilerFlag.cmake files in the GROMACS distribution
- # are used with permission from CMake v2.8.9 so that GROMACS can detect
- # invalid options with the Intel Compilers.
+ # 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.
+ # 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()
  
@@@ -302,12 -363,14 +308,12 @@@ if(GMX_SIMD STREQUAL "AVX_256
  endif()
  
  
 -
 -set(PKG_CFLAGS "")
  if(GMX_DOUBLE)
      add_definitions(-DGMX_DOUBLE)
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_DOUBLE")
 +    list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_DOUBLE")
  endif()
  if(GMX_SOFTWARE_INVSQRT)
 -  set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_SOFTWARE_INVSQRT")
 +    list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_SOFTWARE_INVSQRT")
  endif()
  
  if(WIN32 AND NOT CYGWIN)
@@@ -353,8 -416,6 +359,6 @@@ check_function_exists(filen
  check_function_exists(_commit           HAVE__COMMIT)
  check_function_exists(sigaction         HAVE_SIGACTION)
  check_function_exists(sysconf           HAVE_SYSCONF)
- check_function_exists(sched_setaffinity HAVE_SCHED_SETAFFINITY)
- check_function_exists(sched_getaffinity HAVE_SCHED_GETAFFINITY)
  check_function_exists(rsqrt             HAVE_RSQRT)
  check_function_exists(rsqrtf            HAVE_RSQRTF)
  check_function_exists(sqrtf             HAVE_SQRTF)
@@@ -363,6 -424,9 +367,9 @@@ include(CheckLibraryExists
  check_library_exists(m sqrt "" HAVE_LIBM)
  check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME)
  
+ include(TestSchedAffinity)
+ test_sched_affinity(HAVE_SCHED_AFFINITY)
  include(TestBigEndian)
  test_big_endian(GMX_INTEGER_BIG_ENDIAN)
  
@@@ -474,6 -538,25 +481,6 @@@ if(WIN32 AND NOT CYGWIN
      add_definitions(-DNOMINMAX)
  endif()
  
 -# only bother with finding git and using version.h if the source is a git repo
 -if(GMX_GIT_VERSION_INFO)
 -    if (NOT SOURCE_IS_GIT_REPOSITORY)
 -        message(FATAL_ERROR
 -            "Cannot generate git version information from source tree not under git. "
 -            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 -    endif()
 -    # We need at least git v1.5.3 be able to parse git's date output. If not
 -    # found or the version is too small, we can't generate version information.
 -    find_package(Git)
 -
 -    if(NOT GIT_FOUND OR GIT_VERSION_STRING VERSION_LESS "1.5.3")
 -        message(FATAL_ERROR
 -            "No compatible git version found (>= 1.5.3 required). "
 -            "Won't be able to generate development version information. "
 -            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 -    endif()
 -endif()
 -
  # Detect boost unless GMX_EXTERNAL_BOOST is explicitly OFF
  # Used for default if GMX_EXTERNAL_BOOST is not defined (first CMake pass)
  if(NOT DEFINED GMX_EXTERNAL_BOOST OR GMX_EXTERNAL_BOOST)
@@@ -509,6 -592,12 +516,6 @@@ if (GMX_BUILD_UNITTESTS AND NOT HAVE_LI
          "Either set GMX_BUILD_UNITTESTS=OFF or tell CMake how to find a working version of libxml2.")
  endif()
  
 -########################################################################
 -# Generate development version info for cache
 -########################################################################
 -# set(GEN_VERSION_INFO_INTERNAL "ON")
 -# include(gmxGenerateVersionString)
 -
  ########################################################################
  # Our own GROMACS tests
  ########################################################################
@@@ -518,6 -607,8 +525,6 @@@ include_directories(BEFORE ${CMAKE_SOUR
  include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src/external/thread_mpi/include)
  # Required for config.h, maybe should only be set in src/CMakeLists.txt
  include_directories(BEFORE ${CMAKE_BINARY_DIR}/src)
 -# Required for gmx_header_config_gen.h to be found before installation
 -include_directories(BEFORE ${CMAKE_BINARY_DIR}/src/gromacs/utility)
  # Required for now to make old code compile
  include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src/gromacs/legacyheaders)
  
@@@ -716,6 -807,7 +723,6 @@@ else(
      set(GMX_EXE_LINKER_FLAGS ${GMX_EXE_LINKER_FLAGS} ${OpenMP_LINKER_FLAGS})
      set(GMX_SHARED_LINKER_FLAGS ${GMX_SHARED_LINKER_FLAGS} ${OpenMP_SHARED_LINKER_FLAGS})
  endif()
 -set(PKG_CFLAGS "${PKG_CFLAGS} ${OpenMP_C_FLAGS}")
  
  ########################################################################
  # Specify install locations
@@@ -732,24 -824,17 +739,24 @@@ mark_as_advanced(GMX_LIB_INSTALL_DIR GM
  
  # These variables are used internally to provide a central location for
  # customizing the install locations.
 -set(LIB_INSTALL_DIR  ${GMX_LIB_INSTALL_DIR})
 -set(BIN_INSTALL_DIR  bin)
 -set(DATA_INSTALL_DIR share/${GMX_DATA_INSTALL_DIR})
 -set(MAN_INSTALL_DIR  share/man)
 -set(INCL_INSTALL_DIR include)
 +set(LIB_INSTALL_DIR       ${GMX_LIB_INSTALL_DIR})
 +set(BIN_INSTALL_DIR       bin)
 +set(DATA_INSTALL_DIR      share/${GMX_DATA_INSTALL_DIR})
 +set(MAN_INSTALL_DIR       share/man)
 +# If the nesting level wrt. the installation root is changed,
 +# gromacs-config.cmake.cmakein needs to be adapted.
 +set(CMAKE_INSTALL_DIR     share/cmake)
 +# TODO: Make GMXRC adapt if this is changed
 +set(PKGCONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/pkgconfig)
 +set(INCL_INSTALL_DIR      include)
  
  # These variables get written into config.h for use in finding the data
  # directories.
  set(GMXLIB_SEARCH_DIR share/${GMX_DATA_INSTALL_DIR}/top)
  set(GMXLIB_FALLBACK   ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/top)
  
 +list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${INCL_INSTALL_DIR})
 +
  # Binary and library suffix options
  include(gmxManageSuffixes)
  
@@@ -795,14 -880,15 +802,14 @@@ install(FILES COPYING DESTINATION ${DAT
  
  if(GMX_EXTERNAL_BOOST)
      include_directories(${Boost_INCLUDE_DIRS})
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -I${Boost_INCLUDE_DIRS}")
 +    list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${Boost_INCLUDE_DIRS})
  else()
      include_directories(BEFORE ${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)
 -    # TODO: Propagate the above settings to the installed CMakeFiles.txt template
 -    # (from share/template/)
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -DBOOST_NO_TYPEID -I${CMAKE_INSTALL_PREFIX}/${INCL_INSTALL_DIR}/gromacs/external/boost")
      if (NOT GMX_BUILD_MDRUN_ONLY)
          install(DIRECTORY ${CMAKE_SOURCE_DIR}/src/external/boost/boost
                  DESTINATION ${INCL_INSTALL_DIR}/gromacs/external/boost
index 3e6ed3fc6369af285ce2da7c868abe0ea1012589,0000000000000000000000000000000000000000..7b8d08402f9374a98f8a63bb896650140d1b88fc
mode 100644,000000..100644
--- /dev/null
@@@ -1,408 -1,0 +1,412 @@@
 +#
 +# This file is part of the GROMACS molecular simulation package.
 +#
 +# Copyright (c) 2014, by the GROMACS development team, led by
 +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 +# and including many others, as listed in the AUTHORS file in the
 +# top-level source directory and at http://www.gromacs.org.
 +#
 +# GROMACS is free software; you can redistribute it and/or
 +# modify it under the terms of the GNU Lesser General Public License
 +# as published by the Free Software Foundation; either version 2.1
 +# of the License, or (at your option) any later version.
 +#
 +# GROMACS is distributed in the hope that it will be useful,
 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +# Lesser General Public License for more details.
 +#
 +# You should have received a copy of the GNU Lesser General Public
 +# License along with GROMACS; if not, see
 +# http://www.gnu.org/licenses, or write to the Free Software Foundation,
 +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 +#
 +# If you want to redistribute modifications to GROMACS, please
 +# consider that scientific software is very special. Version
 +# control is crucial - bugs must be traceable. We will be happy to
 +# consider code for inclusion in the official distribution, but
 +# derived work must not be called official GROMACS. Details are found
 +# in the README & COPYING files - if they are missing, get the
 +# official version at http://www.gromacs.org.
 +#
 +# To help us fund GROMACS development, we humbly ask that you cite
 +# the research papers on the package. Check out http://www.gromacs.org.
 +
 +# Sets version information variables and provides CMake functions for
 +# generating files based on them
 +#
 +# This script provides the following basic version variables that need to be
 +# maintained manually:
 +#   GMX_VERSION_MAJOR      Major version number.
 +#   GMX_VERSION_MINOR      Minor version number.
 +#   GMX_VERSION_PATCH      Patch version number.
 +#       Should always be defined: zero for, e.g., 5.0.
 +#   GMX_VERSION_SUFFIX     String suffix to add to numeric version string.
 +#       "-dev" is automatically added when not building from a source package,
 +#       and does not need to be kept here.
 +#   LIBRARY_SOVERSION      so version for the built libraries.
 +#       Should be increased for each binary incompatible release (in GROMACS,
 +#       the typical policy is to increase it for each major/minor version
 +#       change, but not for patch releases, even if the latter may not always
 +#       be fully binary compatible).
 +#   LIBRARY_VERSION        Full library version.
 +#   REGRESSIONTEST_BRANCH  For builds not from source packages, name of the
 +#       regressiontests branch at gerrit.gromacs.org whose HEAD can test this
 +#       code, *if* this code is recent enough (i.e., contains all changes from
 +#       the corresponding code branch that affects the regression test
 +#       results).
++#   REGRESSIONTEST_MD5SUM
++#       The MD5 checksum of the regressiontest tarball. Only used when building
++#       from a source package.
 +# They are collected into a single section below.
 +# The following variables are set based on these:
 +#   GMX_VERSION            String composed from GMX_VERSION_* numeric variables
 +#       above. Example: 4.6.1, 5.0
 +#   GMX_VERSION_STRING     String with GMX_VERSION suffixed with the given
 +#       suffix and possibly "-dev" for builds not from a source package.
 +#   GMX_VERSION_NUMERIC    Numeric version number (e.g., 40601 for 4.6.1).
 +#   GMX_API_VERSION        Numeric API version.
 +#       This is currently set automatically to GMX_VERSION_NUMERIC, but may
 +#       become manually maintained in the future if there will be releases
 +#       where the API does not change, but programs/libraries do.
 +#       In such a case, this should be the first version where the current API
 +#       appeared.
 +#   REGRESSIONTEST_VERSION For source packages, version number of the
 +#       matching regressiontests tarball.  Not used for builds not from source
 +#       packages.
 +# The latter two are used to generate gromacs/version.h to allow software
 +# written against the GROMACS API to provide some #ifdef'ed code to support
 +# multiple GROMACS versions.
 +#
 +# The following variables are defined without manual intervention:
 +#   SOURCE_IS_SOURCE_DISTRIBUTION  The source tree is from a source tarball.
 +#   SOURCE_IS_GIT_REPOSITORY       The source tree is a git repository.
 +# Note that both can be false if the tree has been extracted, e.g., as a
 +# tarball directly from git.
 +#
 +# This script also declares machinery to generate and obtain version
 +# information from a git repository.  This is enabled by default if the source
 +# tree is a git, but can be disabled with
 +#   GMX_GIT_VERSION_INFO           Advanced CMake variable to disable git
 +#                                  version info generation.
 +# The main interface to this machinery is the gmx_configure_version_file()
 +# CMake function.  The signature is
 +#   gmx_configure_version_file(<input> <output>
 +#                              [REMOTE_HASH] [SOURCE_FILE]
 +#                              [TARGET <target>]
 +#                              [COMMENT <comment>])
 +#   <input>      Specify the input and output files as for configure_file().
 +#   <output>     The configuration is done with configure_file(... @ONLY) with
 +#                the following variables defined (as well as all the
 +#                GMX_VERSION* variables from above):
 +#                  GMX_VERSION_STRING_FULL
 +#                  GMX_VERSION_FULL_HASH
 +#                  GMX_VERSION_CENTRAL_BASE_HASH
 +#                The output file is created during build time, so any dependent
 +#                targets should specify it as a dependency.
 +#   REMOTE_HASH  Currently, this has no effect, but it signifies that the
 +#                <input> file is using the CENTRAL_BASE_HASH variable.
 +#                This variable is much more expensive to initialize than the
 +#                others, so this allows local changes in this file to only
 +#                compute that value when required if that becomes necessary.
 +#   SOURCE_FILE  Signals that <output> will be used as a source file.
 +#                The function will set properties for the source file
 +#                appropriately to signify that it is generated.
 +#   TARGET       By default, this function uses add_custom_command() to
 +#                generate the output file.  If TARGET is specified, then
 +#                add_custom_target() is used to create a target with the given
 +#                name <target> that runs this custom command.  Use this if
 +#                the same file will be used for multiple downstream targets,
 +#                or if the explicit target for the file is otherwise
 +#                necessary.
 +#   COMMENT      Set a custom comment to be shown when building the rule
 +#                (see add_custom_command(... COMMENT <comment>)).
 +# As an alternative to using this script, also the following variables are
 +# provided (can be useful when generating more complex CMake scripts that do
 +# build-time tasks):
 +#   VERSION_INFO_CMAKE_SCRIPT
 +#       Absolute path to a CMake script that can be included using include()
 +#       to declare the GMX_VERSION_* variables documented for
 +#       gmx_configure_version_file().
 +#   VERSION_INFO_DEPS
 +#       If a custom command depends on VERSION_INFO_CMAKE_SCRIPT, then it
 +#       should add ${VERSION_INFO_DEPS} to its DEPENDS list to get the
 +#       appropriate dependencies.
 +# TODO: If someone wants to add a custom target that depends on
 +# VERSION_INFO_CMAKE_SCRIPT, a separate variable may be needed for those
 +# dependencies.
 +#
 +# The version string printed by 'gmx -version' (and also printed in the startup
 +# header) can provide useful information for, e.g., diagnosing bug reports and
 +# identifying what exact version the user was using.  The following formats are
 +# possible (with examples given for a particular version):
 +#   4.6.1       Plain version number without any suffix signifies a build from
 +#               a released source tarball.
 +#   4.6.1-dev   '-dev' suffix signifies all other builds. If there is no other
 +#               information, either the user built the code outside any git
 +#               repository, or disabled the version info generation.
 +#   4.6.1-dev-YYYYMMDD-1234abc
 +#               The YYYYMMDD part shows the commit date (not author date) of
 +#               the HEAD commit from which the code was built.  The abbreviated
 +#               hash is the hash of that commit (the full hash is available in
 +#               'gmx -version' output).
 +#               If the HEAD hash is not identified as coming from branches in
 +#               "authoritative" GROMACS repositories, 'gmx -version' will show
 +#               the nearest ancestor commit that is identified as such (but see
 +#               the '-local' and '-unknown' suffixes below).
 +#   4.6.1-dev-YYYYMMDD-1234abc-dirty
 +#               As above, but there were local modifications in the source tree
 +#               when the code was built.
 +#   4.6.1-dev-YYYYMMDD-1234abc-unknown
 +#               As above, but there were no remotes in the repository that
 +#               could be identified as "authoritative" GROMACS repositories.
 +#               This happens if the code is not cloned from git.gromacs.org
 +#               or gerrit.gromacs.org.
 +#   4.6.1-dev-YYYYMMDD-1234abc-local
 +#               As above, but there were no commits in the recent history of
 +#               the branch that could be identified as coming from
 +#               "authoritative" GROMACS repositories.  This should be
 +#               relatively rare.
 +#
 +# Other variables set here are not intended for use outside this file.
 +# The scripts gmxGenerateVersionInfo.cmake and gmxConfigureVersionInfo.cmake
 +# are used internally by this machinery, as well as VersionInfo.cmake.cmakein.
 +
 +#####################################################################
 +# Basic nature of the source tree
 +
 +set(SOURCE_IS_GIT_REPOSITORY OFF)
 +set(SOURCE_IS_SOURCE_DISTRIBUTION OFF)
 +if (EXISTS "${PROJECT_SOURCE_DIR}/.git")
 +    set(SOURCE_IS_GIT_REPOSITORY ON)
 +endif()
 +# This file is excluded from CPack source packages, but part of the repository,
 +# so it should get included everywhere else.
 +if (NOT EXISTS "${PROJECT_SOURCE_DIR}/admin/.isreposource")
 +    set(SOURCE_IS_SOURCE_DISTRIBUTION ON)
 +endif()
 +
 +#####################################################################
 +# Manually maintained version info
 +
 +# The GROMACS convention is that these are the version number of the next
 +# release that is going to be made from this branch.
 +set(GMX_VERSION_MAJOR 5)
 +set(GMX_VERSION_MINOR 1)
 +set(GMX_VERSION_PATCH 0)
 +# The suffix, on the other hand, is used mainly for betas and release
 +# candidates, where it signifies the last such release from this branch;
 +# it will be empty before the first such release, as well as after the
 +# final release is out.
 +set(GMX_VERSION_SUFFIX "")
 +
 +set(LIBRARY_SOVERSION 1)
 +set(LIBRARY_VERSION ${LIBRARY_SOVERSION}.0.0)
 +
 +set(REGRESSIONTEST_BRANCH "refs/heads/master")
++set(REGRESSIONTEST_MD5SUM "a07524afebca5013540d4f2f72df2dce")
 +
 +#####################################################################
 +# General version management based on manually set numbers
 +
 +if (GMX_VERSION_PATCH)
 +    set(GMX_VERSION "${GMX_VERSION_MAJOR}.${GMX_VERSION_MINOR}.${GMX_VERSION_PATCH}")
 +else()
 +    set(GMX_VERSION "${GMX_VERSION_MAJOR}.${GMX_VERSION_MINOR}")
 +endif()
 +set(GMX_VERSION_STRING "${GMX_VERSION}${GMX_VERSION_SUFFIX}")
 +if (NOT SOURCE_IS_SOURCE_DISTRIBUTION)
 +    set(GMX_VERSION_STRING "${GMX_VERSION_STRING}-dev")
 +endif()
 +
 +set(REGRESSIONTEST_VERSION "${GMX_VERSION_STRING}")
 +
 +math(EXPR GMX_VERSION_NUMERIC
 +     "${GMX_VERSION_MAJOR}*10000 + ${GMX_VERSION_MINOR}*100 + ${GMX_VERSION_PATCH}")
 +set(GMX_API_VERSION ${NUM_VERSION})
 +
 +#####################################################################
 +# git version info management
 +
 +# There can be clusters where git and CMake can run on nodes where the other is
 +# not available, accessing the same source tree.
 +# Should be unlikely, but doesn't hurt to check.
 +set(_git_info_default OFF)
 +if (SOURCE_IS_GIT_REPOSITORY)
 +    find_package(Git)
 +    if (GIT_FOUND)
 +        set(_git_info_default ON)
 +    endif()
 +endif()
 +option(GMX_GIT_VERSION_INFO "Generate git version information" ${_git_info_default})
 +mark_as_advanced(GMX_GIT_VERSION_INFO)
 +# Detect preconditions for version info generation if it is requested.
 +if (GMX_GIT_VERSION_INFO)
 +    if (NOT SOURCE_IS_GIT_REPOSITORY)
 +        message(FATAL_ERROR
 +            "Cannot generate git version information from source tree not under git. "
 +            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 +    endif()
 +    # We need at least git v1.5.3 be able to parse git's date output.
 +    if (NOT GIT_FOUND OR GIT_VERSION_STRING VERSION_LESS "1.5.3")
 +        message(FATAL_ERROR
 +            "No compatible git version found (>= 1.5.3 required). "
 +            "Won't be able to generate development version information. "
 +            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 +    endif()
 +endif()
 +
 +# The first two are also for use outside this file, encapsulating the details
 +# of how to use the generated VersionInfo.cmake.
 +set(VERSION_INFO_CMAKE_FILE   ${PROJECT_BINARY_DIR}/VersionInfo.cmake)
 +set(VERSION_INFO_DEPS         ${VERSION_INFO_CMAKE_FILE})
 +# Capture the location of the necessary files in internal variables for use in
 +# the function below.
 +set(VERSION_INFO_CMAKEIN_FILE     ${CMAKE_CURRENT_LIST_DIR}/VersionInfo.cmake.cmakein)
 +set(VERSION_INFO_CONFIGURE_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/gmxConfigureVersionInfo.cmake)
 +
 +# Rules to create the VersionInfo.cmake file.
 +# For git info, the sequence is:
 +#   1. (configure time) VersionInfo.cmake.cmakein -> VersionInfo-partial.cmake.cmakein
 +#        - Set all variables that are known at configure time.
 +#   2. (build time)     VersionInfo-partial.cmake.cmakein -> VersionInfo.cmake
 +#        - Set variables that may change as result of repository state changes
 +#          (i.e., everything that requires running git).
 +#        - Runs every time as a git-version-info target, but the output file
 +#          timestamp only changes if its contents actually change.
 +#        - Depending on the native build system, this may run once per build
 +#          or once per each time it is required for step 3.
 +#   3. (build time)     VersionInfo.cmake -> other files
 +#        - Set variables in files specified with gmx_configure_version_file()
 +#          using the values generated in step 2.
 +#        - Each file runs as a custom command that depends on the previous
 +#          steps, and runs only if the VersionInfo.cmake file is newer than the
 +#          output file.
 +# Without git info, the sequence is:
 +#  1. (configure time) VersionInfo.cmake.cmakein -> VersionInfo.cmake
 +#        - Everything is known at configure time, so the output is generated
 +#          immediately with all variables set (git info will be empty).
 +#  2. (build time)     VersionInfo.cmake -> other files
 +#        - As with git info, processes files from gmx_configure_version_file().
 +#        - These are again custom commands that depend on the output from
 +#          step 1, so they get regenerated only when the static version info
 +#          changes.
 +if (GMX_GIT_VERSION_INFO)
 +    # Configure information known at this time into a partially filled
 +    # version info file.
 +    set(VERSION_INFO_CMAKEIN_FILE_PARTIAL
 +        ${PROJECT_BINARY_DIR}/VersionInfo-partial.cmake.cmakein)
 +    # Leave these to be substituted by the custom target below.
 +    set(GMX_VERSION_STRING_FULL       "\@GMX_VERSION_STRING_FULL\@")
 +    set(GMX_VERSION_FULL_HASH         "\@GMX_VERSION_FULL_HASH\@")
 +    set(GMX_VERSION_CENTRAL_BASE_HASH "\@GMX_VERSION_CENTRAL_BASE_HASH\@")
 +    configure_file(${VERSION_INFO_CMAKEIN_FILE}
 +                   ${VERSION_INFO_CMAKEIN_FILE_PARTIAL}
 +                   @ONLY)
 +    # If generating the version info, create a target that runs on every build
 +    # and does the actual git calls, storing the results into a CMake script.
 +    # This needs to be run at build time to update the version information
 +    # properly when the git hash changes, but the build system does not.
 +    # All targets added by gmx_configure_version_file() use the information
 +    # from this script to get their variables from, removing the need to run
 +    # git multiple times and simplifying reuse for other purposes.
 +    #
 +    # Ninja requires all generated files mentioned in dependencies of custom
 +    # commands (in gmx_configure_version_info()) to be actually mentioned in
 +    # the build system, and luckily add_custom_command() makes that possible.
 +    # But it seems impossible to create a robust custom command that would be
 +    # always run, so other generators that do not have this constraint simply
 +    # use an add_custom_target().
 +    if (CMAKE_GENERATOR STREQUAL "Ninja")
 +        # The second, phony file is never created, so the rule is always
 +        # triggered again.  TODO: Figure out why this works, even though ninja
 +        # very eagerly complains about missing files.
 +        # This unfortunately does not work with the make generator, as
 +        # the non-existent second file causes some part of the generated system
 +        # erase the first file at the beginning of every build, causing a full
 +        # rebuild of the dependencies.
 +        add_custom_command(OUTPUT ${VERSION_INFO_CMAKE_FILE} git-version-phony
 +            COMMAND ${CMAKE_COMMAND}
 +                -D GIT_EXECUTABLE=${GIT_EXECUTABLE}
 +                -D PROJECT_VERSION=${GMX_VERSION_STRING}
 +                -D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
 +                -D VERSION_CMAKEIN=${VERSION_INFO_CMAKEIN_FILE_PARTIAL}
 +                -D VERSION_OUT=${VERSION_INFO_CMAKE_FILE}
 +                -P ${CMAKE_CURRENT_LIST_DIR}/gmxGenerateVersionInfo.cmake
 +            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
 +            COMMENT "Generating git version information"
 +            VERBATIM)
 +        # The generated Ninja build system would probably work fine even
 +        # without this target, but CMake requires all custom commands to belong
 +        # to a target in the same CMakeLists.txt to generate anything for them.
 +        add_custom_target(git-version-info DEPENDS ${VERSION_INFO_CMAKE_FILE})
 +    else()
 +        # For other generators, a target-level dependency on git-version-info
 +        # ensures that VERSION_INFO_CMAKE_FILE is created before the dependent
 +        # target's dependencies are even evaluated.
 +        add_custom_target(git-version-info
 +            COMMAND ${CMAKE_COMMAND}
 +                -D GIT_EXECUTABLE=${GIT_EXECUTABLE}
 +                -D PROJECT_VERSION=${GMX_VERSION_STRING}
 +                -D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
 +                -D VERSION_CMAKEIN=${VERSION_INFO_CMAKEIN_FILE_PARTIAL}
 +                -D VERSION_OUT=${VERSION_INFO_CMAKE_FILE}
 +                -P ${CMAKE_CURRENT_LIST_DIR}/gmxGenerateVersionInfo.cmake
 +            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
 +            COMMENT "Generating git version information"
 +            VERBATIM)
 +    endif()
 +    list(APPEND VERSION_INFO_DEPS git-version-info)
 +else()
 +    # If the version info is static, just generate the CMake script with the
 +    # version variables during the CMake run.
 +    set(GMX_VERSION_STRING_FULL       ${GMX_VERSION_STRING})
 +    set(GMX_VERSION_FULL_HASH         "")
 +    set(GMX_VERSION_CENTRAL_BASE_HASH "")
 +    configure_file(${VERSION_INFO_CMAKEIN_FILE} ${VERSION_INFO_CMAKE_FILE})
 +endif()
 +unset(GMX_VERSION_STRING_FULL)
 +unset(GMX_VERSION_FULL_HASH)
 +unset(GMX_VERSION_CENTRAL_BASE_HASH)
 +
 +# The main user-visible interface to the machinery.
 +# See documentation at the top of the script.
 +function (gmx_configure_version_file INFILE OUTFILE)
 +    include(CMakeParseArguments)
 +    set(_options REMOTE_HASH SOURCE_FILE)
 +    set(_one_value_args COMMENT TARGET)
 +    cmake_parse_arguments(ARG "${_options}" "${_one_value_args}" "" ${ARGN})
 +    if (ARG_UNPARSED_ARGUMENTS)
 +        message(FATAL_ERROR "Unknown arguments: ${ARG_UNPARSED_ARGUMENTS}")
 +    endif()
 +    # Some callers may pass partial paths that do not really make sense,
 +    # so create a default comment that only contains the actual file name.
 +    get_filename_component(_basename ${OUTFILE} NAME)
 +    set(_comment "Generating ${_basename}")
 +    if (ARG_COMMENT)
 +        set(_comment ${ARG_COMMENT})
 +    endif()
 +    # Mimic configure_file()
 +    if (NOT IS_ABSOLUTE ${INFILE})
 +        set(INFILE ${CMAKE_CURRENT_SOURCE_DIR}/${INFILE})
 +    endif()
 +    # The touch command is necessary to ensure that after the target is run,
 +    # the timestamp is newer than in the input files.
 +    add_custom_command(OUTPUT ${OUTFILE}
 +        COMMAND ${CMAKE_COMMAND}
 +            -D VERSION_VARIABLES=${VERSION_INFO_CMAKE_FILE}
 +            -D VERSION_CMAKEIN=${INFILE}
 +            -D VERSION_OUT=${OUTFILE}
 +            -P ${VERSION_INFO_CONFIGURE_SCRIPT}
 +        COMMAND ${CMAKE_COMMAND} -E touch ${OUTFILE}
 +        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
 +        DEPENDS ${INFILE} ${VERSION_INFO_DEPS} ${VERSION_INFO_CONFIGURE_SCRIPT}
 +        COMMENT "${_comment}"
 +        VERBATIM)
 +    if (ARG_TARGET)
 +        add_custom_target(${ARG_TARGET} DEPENDS ${OUTFILE} VERBATIM)
 +    endif()
 +    if (ARG_SOURCE_FILE)
 +        set_source_files_properties(${OUTFILE} PROPERTIES GENERATED true)
 +    endif()
 +endfunction()
index c8858a3bba62308c2707101af0a0e633eec8c16c,211f1af223d734c22ec7bcd29375e450aa90040a..b914782c4bb4dada8c75be1dbe0af8f3ed2c6666
@@@ -223,11 -223,6 +223,6 @@@ recommends eithe
    `cmake -DGMX_BUILD_OWN_FFTW=ON`), or
  * that you build FFTW from the source code.
  
- Note that the GROMACS-managed download of the FFTW tarball has a
- slight chance of posing a security risk. If you use this option, you
- will see a warning that advises how you can eliminate this risk
- (before the opportunity has arisen).
  If you build FFTW from source yourself, get the most recent version
  and follow its [installation
  guide](http://www.fftw.org/doc/Installation-and-Customization.html#Installation-and-Customization).
@@@ -655,13 -650,6 +650,13 @@@ CMakeLists.txt
      is determined by CMake.
      The name of the directory can be changed using `GMX_LIB_INSTALL_DIR` CMake
      variable.
 +`lib/pkgconfig/`
 +  : Information about the installed `libgromacs` library for `pkg-config` is
 +    installed here.  The `lib/` part adapts to the installation location of the
 +    libraries.  The installed files contain the installation prefix as absolute
 +    paths.
 +`share/cmake/`
 +  : CMake package configuration files are installed here.
  `share/gromacs/`
    : Various data files and some documentation go here.
      The `gromacs` part can be changed using `GMX_DATA_INSTALL_DIR`. Using this
diff --combined src/config.h.cmakein
index 2e208b36cdbef9f0046beb3a95ade0abdf641c28,897dae65df052fb935ebf1db01222fcae3deca94..b80401ff66eb3f9903b3f55bc415e7d256b55b2b
  /* Target platform is BlueGene/Q */
  #cmakedefine GMX_TARGET_BGQ
  
 +/** Define if we are building for Cygwin */
 +#cmakedefine GMX_CYGWIN
 +
 +/** Define if we have sufficient C++11 support */
 +#cmakedefine GMX_CXX11
 +
  /* GCC bug in AVX maskload/maskstore arguments - worked around internally */
  #cmakedefine GMX_SIMD_X86_AVX_GCC_MASKLOAD_BUG
  
  /* Define to 1 if you have the sysconf() function */
  #cmakedefine HAVE_SYSCONF
  
- /* Define to 1 if you have the sched_getaffinity() function */
- #cmakedefine HAVE_SCHED_GETAFFINITY
- /* Define to 1 if you have the sched_setaffinity() function */
- #cmakedefine HAVE_SCHED_SETAFFINITY
+ /* Define to 1 if you have the all the affinity functions in sched.h */
+ #cmakedefine HAVE_SCHED_AFFINITY
  
  /* Bytes in IEEE fp word are in big-endian order if set, little-endian if not.
     Only relevant when FLOAT_FORMAT_IEEE754 is defined. */
index 8ddff6fb8c395f21f3b4f5cc7216c9b6af5d833b,944b3089b5ea935dd24de645142239c42ff38dcd..66c0f2abada26e0021ab6b58c3d363536ca6b665
  /* This file is completely threadsafe - keep it that way! */
  #include "gromacs/commandline/pargs.h"
  
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
 -#include <cctype>
 -#include <cstdio>
  #include <cstdlib>
  #include <cstring>
  
 +#include <algorithm>
 +#include <list>
 +
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  
  #include "thread_mpi/threads.h"
  
 -#include "gromacs/legacyheaders/gmx_fatal.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/network.h"
 -
  #include "gromacs/commandline/cmdlinehelpcontext.h"
  #include "gromacs/commandline/cmdlinehelpwriter.h"
 -#include "gromacs/commandline/shellcompletions.h"
 +#include "gromacs/commandline/cmdlineparser.h"
  #include "gromacs/fileio/timecontrol.h"
  #include "gromacs/options/basicoptions.h"
  #include "gromacs/options/filenameoption.h"
 +#include "gromacs/options/filenameoptionmanager.h"
  #include "gromacs/options/options.h"
 +#include "gromacs/options/timeunitmanager.h"
  #include "gromacs/utility/arrayref.h"
 +#include "gromacs/utility/basenetwork.h"
 +#include "gromacs/utility/common.h"
  #include "gromacs/utility/cstringutil.h"
  #include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/programcontext.h"
  #include "gromacs/utility/smalloc.h"
  /* The source code in this file should be thread-safe.
        Please keep it that way. */
  
 -static void usage(const char *type, const char *arg)
 -{
 -    GMX_ASSERT(arg != NULL, "NULL command-line argument should not occur");
 -    gmx_fatal(FARGS, "Expected %s argument for option %s\n", type, arg);
 -}
 -
 -/* Scan an int for argument argv[*i] from argument at argv[*i + 1].
 - * eg: -p 32.  argv[*i] is only used for error reporting.
 - * If there is no value, or the conversion is not successful, the
 - * routine exits with an error, otherwise it returns the value found.
 - * *i is incremented once.
 - */
 -static int iscan(int argc, char *argv[], int *i)
 -{
 -    const char *const arg = argv[*i];
 -    if (argc <= (*i)+1)
 -    {
 -        usage("an integer", arg);
 -    }
 -    const char *const value = argv[++(*i)];
 -    char             *endptr;
 -    int               var = std::strtol(value, &endptr, 10);
 -    if (*value == '\0' || *endptr != '\0')
 -    {
 -        usage("an integer", arg);
 -    }
 -    return var;
 -}
 -
 -/* Same as above, but for large integer values */
 -static gmx_int64_t istepscan(int argc, char *argv[], int *i)
 -{
 -    const char *const arg = argv[*i];
 -    if (argc <= (*i)+1)
 -    {
 -        usage("an integer", arg);
 -    }
 -    const char *const value = argv[++(*i)];
 -    char             *endptr;
 -    gmx_int64_t       var = str_to_int64_t(value, &endptr);
 -    if (*value == '\0' || *endptr != '\0')
 -    {
 -        usage("an integer", arg);
 -    }
 -    return var;
 -}
 -
 -/* Routine similar to the above, but working on doubles. */
 -static double dscan(int argc, char *argv[], int *i)
 -{
 -    const char *const arg = argv[*i];
 -    if (argc <= (*i)+1)
 -    {
 -        usage("a real", arg);
 -    }
 -    const char *const value = argv[++(*i)];
 -    char             *endptr;
 -    double            var = std::strtod(value, &endptr);
 -    if (*value == '\0' || *endptr != '\0')
 -    {
 -        usage("a real", arg);
 -    }
 -    return var;
 -}
 -
 -/* Routine similar to the above, but working on strings. The pointer
 - * returned is a pointer to the argv field.
 - */
 -static char *sscan(int argc, char *argv[], int *i)
 -{
 -    if (argc > (*i)+1)
 -    {
 -        if ( (argv[(*i)+1][0] == '-') && (argc > (*i)+2) &&
 -             (argv[(*i)+2][0] != '-') )
 -        {
 -            fprintf(stderr, "Possible missing string argument for option %s\n\n",
 -                    argv[*i]);
 -        }
 -    }
 -    else
 -    {
 -        usage("a string", argv[*i]);
 -    }
 -
 -    return argv[++(*i)];
 -}
 -
 -static gmx_bool is_hidden(t_pargs *pa)
 -{
 -    return (strstr(pa->desc, "HIDDEN") != NULL);
 -}
 -
  int nenum(const char *const enumc[])
  {
      int i;
      return i;
  }
  
 -/* Read a number of arguments from the command line.
 - * For etINT, etREAL and etCHAR an extra argument is read (when present)
 - * for etBOOL the gmx_boolean option is changed to the negate value
 - */
 -static void get_pargs(int *argc, char *argv[], int nparg, t_pargs pa[])
 -{
 -    int       i, j, k, match;
 -    gmx_bool *bKeep;
 -    char      buf[32];
 -    char     *ptr;
 -
 -    snew(bKeep, *argc+1);
 -    bKeep[0]     = TRUE;
 -    bKeep[*argc] = TRUE;
 -
 -    for (i = 1; (i < *argc); i++)
 -    {
 -        bKeep[i] = TRUE;
 -        for (j = 0; (j < nparg); j++)
 -        {
 -            if (pa[j].type == etBOOL)
 -            {
 -                sprintf(buf, "-no%s", pa[j].option+1);
 -                if (strcmp(pa[j].option, argv[i]) == 0)
 -                {
 -                    *pa[j].u.b = TRUE;
 -                    pa[j].bSet = TRUE;
 -                    bKeep[i]   = FALSE;
 -                }
 -                else if (strcmp(buf, argv[i]) == 0)
 -                {
 -                    *pa[j].u.b = FALSE;
 -                    pa[j].bSet = TRUE;
 -                    bKeep[i]   = FALSE;
 -                }
 -            }
 -            else if (strcmp(pa[j].option, argv[i]) == 0)
 -            {
 -                if (pa[j].bSet)
 -                {
 -                    fprintf(stderr, "Setting option %s more than once!\n",
 -                            pa[j].option);
 -                }
 -                pa[j].bSet = TRUE;
 -                bKeep[i]   = FALSE;
 -                switch (pa[j].type)
 -                {
 -                    case etINT:
 -                        *pa[j].u.i = iscan(*argc, argv, &i);
 -                        break;
 -                    case etINT64:
 -                        *pa[j].u.is = istepscan(*argc, argv, &i);
 -                        break;
 -                    case etTIME:
 -                    case etREAL:
 -                        *pa[j].u.r = dscan(*argc, argv, &i);
 -                        break;
 -                    case etSTR:
 -                        *(pa[j].u.c) = sscan(*argc, argv, &i);
 -                        break;
 -                    case etENUM:
 -                        match = -1;
 -                        ptr   = sscan(*argc, argv, &i);
 -                        for (k = 1; (pa[j].u.c[k] != NULL); k++)
 -                        {
 -                            /* only check ptr against beginning of
 -                               pa[j].u.c[k] */
 -                            if (gmx_strncasecmp(ptr, pa[j].u.c[k], strlen(ptr)) == 0)
 -                            {
 -                                if ( ( match == -1 ) ||
 -                                     ( strlen(pa[j].u.c[k]) <
 -                                       strlen(pa[j].u.c[match]) ) )
 -                                {
 -                                    match = k;
 -                                }
 -                            }
 -                        }
 -                        if (match != -1)
 -                        {
 -                            pa[j].u.c[0] = pa[j].u.c[match];
 -                        }
 -                        else
 -                        {
 -                            gmx_fatal(FARGS, "Invalid argument %s for option %s",
 -                                      ptr, pa[j].option);
 -                        }
 -                        break;
 -                    case etRVEC:
 -                        (*pa[j].u.rv)[0] = dscan(*argc, argv, &i);
 -                        if ( (i+1 == *argc) ||
 -                             ( (argv[i+1][0] == '-') &&
 -                               !isdigit(argv[i+1][1]) ) )
 -                        {
 -                            (*pa[j].u.rv)[1]     =
 -                                (*pa[j].u.rv)[2] =
 -                                    (*pa[j].u.rv)[0];
 -                        }
 -                        else
 -                        {
 -                            bKeep[i]         = FALSE;
 -                            (*pa[j].u.rv)[1] = dscan(*argc, argv, &i);
 -                            if ( (i+1 == *argc) ||
 -                                 ( (argv[i+1][0] == '-') &&
 -                                   !isdigit(argv[i+1][1]) ) )
 -                            {
 -                                gmx_fatal(FARGS,
 -                                          "%s: vector must have 1 or 3 real parameters",
 -                                          pa[j].option);
 -                            }
 -                            bKeep[i]         = FALSE;
 -                            (*pa[j].u.rv)[2] = dscan(*argc, argv, &i);
 -                        }
 -                        break;
 -                    default:
 -                        gmx_fatal(FARGS, "Invalid type %d in pargs", pa[j].type);
 -                }
 -                /* i may be incremented, so set it to not keep */
 -                bKeep[i] = FALSE;
 -            }
 -        }
 -    }
 -
 -    /* Remove used entries */
 -    for (i = j = 0; (i <= *argc); i++)
 -    {
 -        if (bKeep[i])
 -        {
 -            argv[j++] = argv[i];
 -        }
 -    }
 -    (*argc) = j-1;
 -    sfree(bKeep);
 -}
 -
  int opt2parg_int(const char *option, int nparg, t_pargs pa[])
  {
      int i;
@@@ -194,141 -420,89 +194,141 @@@ const char *opt2parg_enum(const char *o
   * parse_common_args()
   */
  
 -static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
 +namespace gmx
  {
 -    int         i      = 0;
 -    const char *select = NULL;
 -
 -    if (bCanTime)
 -    {
 -        select = getenv("GMXTIMEUNIT");
 -        if (select != NULL)
 -        {
 -            i = 1;
 -            while (time_list[i] && strcmp(time_list[i], select) != 0)
 -            {
 -                i++;
 -            }
 -        }
 -    }
 -    if (!bCanTime || select == NULL ||
 -        time_list[i] == NULL || strcmp(time_list[i], select) != 0)
 -    {
 -        /* Set it to the default: ps */
 -        i = 1;
 -        while (time_list[i] && strcmp(time_list[i], "ps") != 0)
 -        {
 -            i++;
 -        }
 -
 -    }
 -    time_list[0] = time_list[i];
 -}
  
 -static void set_default_xvg_format(const char *xvg_list[])
 +namespace
  {
 -    int         i;
 -    const char *select;
  
 -    select = getenv("GMX_VIEW_XVG");
 -    if (select == NULL)
 -    {
 -        /* The default is the first option */
 -        xvg_list[0] = xvg_list[1];
 -    }
 -    else
 +/*! \brief
 + * Returns the index of the default xvg format.
 + *
 + * \ingroup module_commandline
 + */
 +int getDefaultXvgFormat(gmx::ConstArrayRef<const char *> xvgFormats)
 +{
 +    const char *const select = getenv("GMX_VIEW_XVG");
 +    if (select != NULL)
      {
 -        i = 1;
 -        while (xvg_list[i] && strcmp(xvg_list[i], select) != 0)
 -        {
 -            i++;
 -        }
 -        if (xvg_list[i] != NULL)
 +        ConstArrayRef<const char *>::const_iterator i =
 +            std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
 +        if (i != xvgFormats.end())
          {
 -            xvg_list[0] = xvg_list[i];
 +            return i - xvgFormats.begin();
          }
          else
          {
 -            xvg_list[0] = xvg_list[exvgNONE];
 +            return exvgNONE - 1;
          }
      }
 +    /* The default is the first option */
 +    return 0;
  }
  
 -static int add_parg(int npargs, t_pargs *pa, t_pargs *pa_add)
 -{
 -    memcpy(&(pa[npargs]), pa_add, sizeof(*pa_add));
 -
 -    return npargs+1;
 -}
 -
 -namespace gmx
 -{
 -
 -namespace
 -{
 -
  /*! \brief
 - * Converts a t_filenm option into an Options option.
 + * Conversion helper between t_pargs/t_filenm and Options.
   *
 - * \param     options Options object to add the new option to.
 - * \param[in] fnm     t_filenm option to convert.
 + * This class holds the necessary mapping between the old C structures and
 + * the new C++ options to allow copying values back after parsing for cases
 + * where the C++ options do not directly provide the type of value required for
 + * the C structures.
   *
   * \ingroup module_commandline
   */
 -void filenmToOptions(Options *options, const t_filenm *fnm)
 +class OptionsAdapter
  {
 +    public:
 +        /*! \brief
 +         * Initializes the adapter to convert from a specified command line.
 +         *
 +         * The command line is required, because t_pargs wants to return
 +         * strings by reference to the original command line.
 +         * OptionsAdapter creates a copy of the `argv` array (but not the
 +         * strings) to make this possible, even if the parser removes
 +         * options it has recognized.
 +         */
 +        OptionsAdapter(int argc, const char *const argv[])
 +            : argv_(argv, argv + argc)
 +        {
 +        }
 +
 +        /*! \brief
 +         * Converts a t_filenm option into an Options option.
 +         *
 +         * \param options Options object to add the new option to.
 +         * \param fnm     t_filenm option to convert.
 +         */
 +        void filenmToOptions(Options *options, t_filenm *fnm);
 +        /*! \brief
 +         * Converts a t_pargs option into an Options option.
 +         *
 +         * \param     options Options object to add the new option to.
 +         * \param     pa      t_pargs option to convert.
 +         */
 +        void pargsToOptions(Options *options, t_pargs *pa);
 +
 +        /*! \brief
 +         * Copies values back from options to t_pargs/t_filenm.
 +         */
 +        void copyValues(bool bReadNode);
 +
 +    private:
 +        struct FileNameData
 +        {
 +            //! Creates a conversion helper for a given `t_filenm` struct.
 +            explicit FileNameData(t_filenm *fnm) : fnm(fnm), optionInfo(NULL)
 +            {
 +            }
 +
 +            //! t_filenm structure to receive the final values.
 +            t_filenm                 *fnm;
 +            //! Option info object for the created FileNameOption.
 +            FileNameOptionInfo       *optionInfo;
 +            //! Value storage for the created FileNameOption.
 +            std::vector<std::string>  values;
 +        };
 +        struct ProgramArgData
 +        {
 +            //! Creates a conversion helper for a given `t_pargs` struct.
 +            explicit ProgramArgData(t_pargs *pa)
 +                : pa(pa), optionInfo(NULL), enumIndex(0), boolValue(false)
 +            {
 +            }
 +
 +            //! t_pargs structure to receive the final values.
 +            t_pargs                 *pa;
 +            //! Option info object for the created option.
 +            OptionInfo              *optionInfo;
 +            //! Value storage for a non-enum StringOption (unused for other types).
 +            std::string              stringValue;
 +            //! Value storage for an enum option (unused for other types).
 +            int                      enumIndex;
 +            //! Value storage for a BooleanOption (unused for other types).
 +            bool                     boolValue;
 +        };
 +
 +        std::vector<const char *>    argv_;
 +        // These are lists instead of vectors to avoid relocating existing
 +        // objects in case the container is reallocated (the Options object
 +        // contains pointes to members of the objects, which would get
 +        // invalidated).
 +        std::list<FileNameData>      fileNameOptions_;
 +        std::list<ProgramArgData>    programArgs_;
 +
 +        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsAdapter);
 +};
 +
 +void OptionsAdapter::filenmToOptions(Options *options, t_filenm *fnm)
 +{
 +    if (fnm->opt == NULL)
 +    {
 +        // Existing code may use opt2fn() instead of ftp2fn() for
 +        // options that use the default option name, so we need to
 +        // keep the old behavior instead of fixing opt2fn().
 +        // TODO: Check that this is not the case, remove this, and make
 +        // opt2*() work even if fnm->opt is NULL for some options.
 +        fnm->opt = ftp2defopt(fnm->ftp);
 +    }
      const bool        bRead     = ((fnm->flag & ffREAD)  != 0);
      const bool        bWrite    = ((fnm->flag & ffWRITE) != 0);
      const bool        bOptional = ((fnm->flag & ffOPT)   != 0);
      {
          defName = ftp2defnm(fnm->ftp);
      }
 -    // Since we are not (yet) using this for actual parsing, we don't need to
 -    // set any storage.
 -    options->addOption(
 -            FileNameOption(name).defaultBasename(defName).legacyType(fnm->ftp)
 -                .readWriteFlags(bRead, bWrite).required(!bOptional)
 -                .libraryFile(bLibrary).multiValue(bMultiple)
 -                .description(ftp2desc(fnm->ftp)));
 +    fileNameOptions_.push_back(FileNameData(fnm));
 +    FileNameData &data = fileNameOptions_.back();
 +    data.optionInfo = options->addOption(
 +                FileNameOption(name).storeVector(&data.values)
 +                    .defaultBasename(defName).legacyType(fnm->ftp)
 +                    .legacyOptionalBehavior()
 +                    .readWriteFlags(bRead, bWrite).required(!bOptional)
 +                    .libraryFile(bLibrary).multiValue(bMultiple)
 +                    .description(ftp2desc(fnm->ftp)));
  }
  
 -/*! \brief
 - * Converts a t_pargs option into an Options option.
 - *
 - * \param     options Options object to add the new option to.
 - * \param[in] pa      t_pargs option to convert.
 - *
 - * \ingroup module_commandline
 - */
 -void pargsToOptions(Options *options, t_pargs *pa)
 +void OptionsAdapter::pargsToOptions(Options *options, t_pargs *pa)
  {
 -    const bool        bHidden = is_hidden(pa);
 +    const bool        bHidden = startsWith(pa->desc, "HIDDEN");
      const char *const name    = &pa->option[1];
      const char *const desc    = (bHidden ? &pa->desc[6] : pa->desc);
 -    // Since we are not (yet) using this for actual parsing, we can take some
 -    // shortcuts and not set any storage where there is no direct
 -    // correspondence in the types.
 +    programArgs_.push_back(ProgramArgData(pa));
 +    ProgramArgData   &data = programArgs_.back();
      switch (pa->type)
      {
          case etINT:
 -            options->addOption(
 -                IntegerOption(name).store(pa->u.i)
 -                    .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        IntegerOption(name).store(pa->u.i)
 +                            .description(desc).hidden(bHidden));
              return;
          case etINT64:
 -            options->addOption(
 -                Int64Option(name).store(pa->u.is)
 -                    .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        Int64Option(name).store(pa->u.is)
 +                            .description(desc).hidden(bHidden));
              return;
          case etREAL:
 -            options->addOption(
 -                RealOption(name).store(pa->u.r)
 -                    .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        RealOption(name).store(pa->u.r)
 +                            .description(desc).hidden(bHidden));
              return;
          case etTIME:
 -            options->addOption(
 -                RealOption(name).store(pa->u.r).timeValue()
 -                    .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        RealOption(name).store(pa->u.r).timeValue()
 +                            .description(desc).hidden(bHidden));
              return;
          case etSTR:
          {
              const char *const defValue = (*pa->u.c != NULL ? *pa->u.c : "");
 -            options->addOption(
 -                    StringOption(name).defaultValue(defValue)
 -                        .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        StringOption(name).store(&data.stringValue)
 +                            .defaultValue(defValue)
 +                            .description(desc).hidden(bHidden));
              return;
          }
          case etBOOL:
 -            options->addOption(
 -                BooleanOption(name).defaultValue(*pa->u.b)
 -                    .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        BooleanOption(name).store(&data.boolValue)
 +                            .defaultValue(*pa->u.b)
 +                            .description(desc).hidden(bHidden));
              return;
          case etRVEC:
 -            options->addOption(
 -                RealOption(name).store(*pa->u.rv).vector()
 -                    .description(desc).hidden(bHidden));
 +            data.optionInfo = options->addOption(
 +                        RealOption(name).store(*pa->u.rv).vector()
 +                            .description(desc).hidden(bHidden));
              return;
          case etENUM:
 -            options->addOption(
 -                StringOption(name).defaultEnumIndex(nenum(pa->u.c) - 1)
 -                    .enumValueFromNullTerminatedArray(pa->u.c + 1)
 -                    .description(desc).hidden(bHidden));
 +        {
 +            const int defaultIndex = (pa->u.c[0] != NULL ? nenum(pa->u.c) - 1 : 0);
 +            data.optionInfo = options->addOption(
 +                        StringOption(name).storeEnumIndex(&data.enumIndex)
 +                            .defaultEnumIndex(defaultIndex)
 +                            .enumValueFromNullTerminatedArray(pa->u.c + 1)
 +                            .description(desc).hidden(bHidden));
              return;
 +        }
      }
      GMX_THROW(NotImplementedError("Argument type not implemented"));
  }
  
 +void OptionsAdapter::copyValues(bool bReadNode)
 +{
 +    std::list<FileNameData>::const_iterator file;
 +    for (file = fileNameOptions_.begin(); file != fileNameOptions_.end(); ++file)
 +    {
 +        if (!bReadNode && (file->fnm->flag & ffREAD))
 +        {
 +            continue;
 +        }
 +        if (file->optionInfo->isSet())
 +        {
 +            file->fnm->flag |= ffSET;
 +        }
 +        file->fnm->nfiles = file->values.size();
 +        snew(file->fnm->fns, file->fnm->nfiles);
 +        for (int i = 0; i < file->fnm->nfiles; ++i)
 +        {
 +            file->fnm->fns[i] = gmx_strdup(file->values[i].c_str());
 +        }
 +    }
 +    std::list<ProgramArgData>::const_iterator arg;
 +    for (arg = programArgs_.begin(); arg != programArgs_.end(); ++arg)
 +    {
 +        arg->pa->bSet = arg->optionInfo->isSet();
 +        switch (arg->pa->type)
 +        {
 +            case etSTR:
 +            {
 +                if (arg->pa->bSet)
 +                {
 +                    std::vector<const char *>::const_iterator pos =
 +                        std::find(argv_.begin(), argv_.end(), arg->stringValue);
 +                    GMX_RELEASE_ASSERT(pos != argv_.end(),
 +                                       "String argument got a value not in argv");
 +                    *arg->pa->u.c = *pos;
 +                }
 +                break;
 +            }
 +            case etBOOL:
 +                *arg->pa->u.b = arg->boolValue;
 +                break;
 +            case etENUM:
 +                *arg->pa->u.c = arg->pa->u.c[arg->enumIndex + 1];
 +                break;
 +            default:
 +                // For other types, there is nothing type-specific to do.
 +                break;
 +        }
 +    }
 +}
 +
  } // namespace
  
  } // namespace gmx
@@@ -476,160 -600,280 +476,160 @@@ gmx_bool parse_common_args(int *argc, c
                             output_env_t *oenv)
  {
      /* This array should match the order of the enum in oenv.h */
 -    const char *xvg_format[] = { NULL, "xmgrace", "xmgr", "none", NULL };
 -    /* This array should match the order of the enum in oenv.h */
 -    const char *time_units[] = {
 -        NULL, "fs", "ps", "ns", "us", "ms", "s",
 -        NULL
 -    };
 -    int         nicelevel = 0, debug_level = 0;
 -    char       *deffnm    = NULL;
 -    real        tbegin    = 0, tend = 0, tdelta = 0;
 -    gmx_bool    bView     = FALSE;
 -
 -    t_pargs    *all_pa = NULL;
 -
 -    t_pargs     nice_pa   = {
 -        "-nice", FALSE, etINT,   {&nicelevel},
 -        "Set the nicelevel"
 -    };
 -    t_pargs     deffnm_pa = {
 -        "-deffnm", FALSE, etSTR, {&deffnm},
 -        "Set the default filename for all file options"
 -    };
 -    t_pargs     begin_pa  = {
 -        "-b",    FALSE, etTIME,  {&tbegin},
 -        "First frame (%t) to read from trajectory"
 -    };
 -    t_pargs     end_pa    = {
 -        "-e",    FALSE, etTIME,  {&tend},
 -        "Last frame (%t) to read from trajectory"
 -    };
 -    t_pargs     dt_pa     = {
 -        "-dt",   FALSE, etTIME,  {&tdelta},
 -        "Only use frame when t MOD dt = first time (%t)"
 -    };
 -    t_pargs     view_pa   = {
 -        "-w",    FALSE, etBOOL,  {&bView},
 -        "View output [TT].xvg[tt], [TT].xpm[tt], [TT].eps[tt] and [TT].pdb[tt] files"
 -    };
 -    t_pargs     xvg_pa    = {
 -        "-xvg",  FALSE, etENUM,  {xvg_format},
 -        "xvg plot formatting"
 -    };
 -    t_pargs     time_pa   = {
 -        "-tu",   FALSE, etENUM,  {time_units},
 -        "Time unit"
 -    };
 -    /* Maximum number of extra arguments */
 -#define EXTRA_PA 16
 -
 -    t_pargs  pca_pa[] = {
 -        { "-debug", FALSE, etINT, {&debug_level},
 -          "HIDDENWrite file with debug information, 1: short, 2: also x and f" },
 -    };
 -#define NPCA_PA asize(pca_pa)
 -    gmx_bool bXvgr;
 -    int      i, j, k, npall, max_pa;
 +    const char *const xvg_formats[] = { "xmgrace", "xmgr", "none" };
  
      // Handle the flags argument, which is a bit field
      // The FF macro returns whether or not the bit is set
  #define FF(arg) ((Flags & arg) == arg)
  
 -    /* Check for double arguments */
 -    for (i = 1; (i < *argc); i++)
 +    try
      {
 -        if (argv[i] && (strlen(argv[i]) > 1) && (!std::isdigit(argv[i][1])))
 +        int                        nicelevel = 0;
 +        double                     tbegin    = 0.0, tend = 0.0, tdelta = 0.0;
 +        bool                       bView     = false;
 +        int                        xvgFormat = 0;
 +        gmx::TimeUnitManager       timeUnitManager;
 +        gmx::OptionsAdapter        adapter(*argc, argv);
 +        gmx::Options               options(NULL, NULL);
 +        gmx::FileNameOptionManager fileOptManager;
 +
 +        fileOptManager.disableInputOptionChecking(
 +                FF(PCA_NOT_READ_NODE) || FF(PCA_DISABLE_INPUT_FILE_CHECKING));
 +        options.addManager(&fileOptManager);
-         options.setDescription(gmx::ConstArrayRef<const char *>(desc, ndesc));
++        options.setDescription(gmx::constArrayRefFromArray<const char *>(desc, ndesc));
 +
 +        options.addOption(
 +                gmx::IntegerOption("nice").store(&nicelevel)
 +                    .defaultValue(FF(PCA_BE_NICE) ? 19 : 0)
 +                    .description("Set the nicelevel"));
 +
 +        if (FF(PCA_CAN_SET_DEFFNM))
          {
 -            for (j = i+1; (j < *argc); j++)
 -            {
 -                if ( (argv[i][0] == '-') && (argv[j][0] == '-') &&
 -                     (strcmp(argv[i], argv[j]) == 0) )
 -                {
 -                    if (FF(PCA_NOEXIT_ON_ARGS))
 -                    {
 -                        fprintf(stderr, "Double command line argument %s\n",
 -                                argv[i]);
 -                    }
 -                    else
 -                    {
 -                        gmx_fatal(FARGS, "Double command line argument %s\n",
 -                                  argv[i]);
 -                    }
 -                }
 -            }
 +            fileOptManager.addDefaultFileNameOption(&options, "deffnm");
          }
 -    }
 -    debug_gmx();
 -
 -    /* Check ALL the flags ... */
 -    max_pa = NPCA_PA + EXTRA_PA + npargs+1;
 -    snew(all_pa, max_pa);
 -
 -    for (i = npall = 0; (i < static_cast<int>(NPCA_PA)); i++)
 -    {
 -        npall = add_parg(npall, all_pa, &(pca_pa[i]));
 -    }
 -
 -    if (FF(PCA_BE_NICE))
 -    {
 -        nicelevel = 19;
 -    }
 -    npall = add_parg(npall, all_pa, &nice_pa);
 -
 -    if (FF(PCA_CAN_SET_DEFFNM))
 -    {
 -        npall = add_parg(npall, all_pa, &deffnm_pa);
 -    }
 -    if (FF(PCA_CAN_BEGIN))
 -    {
 -        npall = add_parg(npall, all_pa, &begin_pa);
 -    }
 -    if (FF(PCA_CAN_END))
 -    {
 -        npall = add_parg(npall, all_pa, &end_pa);
 -    }
 -    if (FF(PCA_CAN_DT))
 -    {
 -        npall = add_parg(npall, all_pa, &dt_pa);
 -    }
 -    if (FF(PCA_TIME_UNIT))
 -    {
 -        npall = add_parg(npall, all_pa, &time_pa);
 -    }
 -    if (FF(PCA_CAN_VIEW))
 -    {
 -        npall = add_parg(npall, all_pa, &view_pa);
 -    }
 -
 -    bXvgr = FALSE;
 -    for (i = 0; (i < nfile); i++)
 -    {
 -        bXvgr = bXvgr ||  (fnm[i].ftp == efXVG);
 -    }
 -    if (bXvgr)
 -    {
 -        npall = add_parg(npall, all_pa, &xvg_pa);
 -    }
 -
 -    /* Now append the program specific arguments */
 -    for (i = 0; (i < npargs); i++)
 -    {
 -        npall = add_parg(npall, all_pa, &(pa[i]));
 -    }
 -
 -    /* set etENUM options to default */
 -    for (i = 0; (i < npall); i++)
 -    {
 -        if (all_pa[i].type == etENUM)
 +        if (FF(PCA_CAN_BEGIN))
          {
 -            all_pa[i].u.c[0] = all_pa[i].u.c[1];
 +            options.addOption(
 +                    gmx::DoubleOption("b").store(&tbegin).timeValue()
 +                        .description("First frame (%t) to read from trajectory"));
          }
 -    }
 -    set_default_time_unit(time_units, FF(PCA_TIME_UNIT));
 -    set_default_xvg_format(xvg_format);
 -
 -    /* Now parse all the command-line options */
 -    get_pargs(argc, argv, npall, all_pa);
 -
 -    /* set program name, command line, and default values for output options */
 -    output_env_init(oenv, gmx::getProgramContext(), (time_unit_t)nenum(time_units), bView,
 -                    (xvg_format_t)nenum(xvg_format), 0, debug_level);
 -
 -    /* Parse the file args */
 -    parse_file_args(argc, argv, nfile, fnm, deffnm, !FF(PCA_NOT_READ_NODE));
 -
 -    /* Open the debug file */
 -    if (debug_level > 0)
 -    {
 -        char buf[256];
 -
 -        if (gmx_mpi_initialized())
 +        if (FF(PCA_CAN_END))
          {
 -            sprintf(buf, "%s%d.debug", output_env_get_short_program_name(*oenv),
 -                    gmx_node_rank());
 +            options.addOption(
 +                    gmx::DoubleOption("e").store(&tend).timeValue()
 +                        .description("Last frame (%t) to read from trajectory"));
          }
 -        else
 +        if (FF(PCA_CAN_DT))
 +        {
 +            options.addOption(
 +                    gmx::DoubleOption("dt").store(&tdelta).timeValue()
 +                        .description("Only use frame when t MOD dt = first time (%t)"));
 +        }
 +        if (FF(PCA_TIME_UNIT))
 +        {
 +            timeUnitManager.setTimeUnitFromEnvironment();
 +            timeUnitManager.addTimeUnitOption(&options, "tu");
 +        }
 +        if (FF(PCA_CAN_VIEW))
          {
 -            sprintf(buf, "%s.debug", output_env_get_short_program_name(*oenv));
 +            options.addOption(
 +                    gmx::BooleanOption("w").store(&bView)
 +                        .description("View output [TT].xvg[tt], [TT].xpm[tt], "
 +                                     "[TT].eps[tt] and [TT].pdb[tt] files"));
          }
  
 -        init_debug(debug_level, buf);
 -        fprintf(stderr, "Opening debug file %s (src code file %s, line %d)\n",
 -                buf, __FILE__, __LINE__);
 -    }
 +        bool bXvgr = false;
 +        for (int i = 0; i < nfile; i++)
 +        {
 +            bXvgr = bXvgr || (fnm[i].ftp == efXVG);
 +        }
 +        xvgFormat = gmx::getDefaultXvgFormat(xvg_formats);
 +        if (bXvgr)
 +        {
 +            options.addOption(
 +                    gmx::StringOption("xvg").enumValue(xvg_formats)
 +                        .storeEnumIndex(&xvgFormat)
 +                        .description("xvg plot formatting"));
 +        }
  
 -    /* Now copy the results back... */
 -    for (i = 0, k = npall-npargs; (i < npargs); i++, k++)
 -    {
 -        memcpy(&(pa[i]), &(all_pa[k]), (size_t)sizeof(pa[i]));
 -    }
 +        /* Now append the program specific arguments */
 +        for (int i = 0; i < nfile; i++)
 +        {
 +            adapter.filenmToOptions(&options, &fnm[i]);
 +        }
 +        for (int i = 0; i < npargs; i++)
 +        {
 +            adapter.pargsToOptions(&options, &pa[i]);
 +        }
  
 -    bool bExit = false;
 -    try
 -    {
          const gmx::CommandLineHelpContext *context =
              gmx::GlobalCommandLineHelpContext::get();
 -        bExit = (context != NULL);
 -        if (context != NULL && !(FF(PCA_QUIET)))
 +        if (context != NULL)
          {
 -            gmx::Options options(NULL, NULL);
 -            options.setDescription(gmx::constArrayRefFromArray(desc, ndesc));
 -            for (i = 0; i < nfile; i++)
 -            {
 -                gmx::filenmToOptions(&options, &fnm[i]);
 -            }
 -            for (i = 0; i < npall; i++)
 -            {
 -                gmx::pargsToOptions(&options, &all_pa[i]);
 -            }
 +            GMX_RELEASE_ASSERT(gmx_node_rank() == 0,
 +                               "Help output should be handled higher up and "
 +                               "only get called only on the master rank");
              gmx::CommandLineHelpWriter(options)
                  .setShowDescriptions(true)
 -                .setTimeUnitString(output_env_get_time_unit(*oenv))
 +                .setTimeUnitString(timeUnitManager.timeUnitAsString())
-                 .setKnownIssues(gmx::ConstArrayRef<const char *>(bugs, nbugs))
+                 .setKnownIssues(gmx::constArrayRefFromArray(bugs, nbugs))
                  .writeHelp(*context);
 +            return FALSE;
          }
 -    }
 -    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
  
 -    /* Set the nice level */
 +        /* Now parse all the command-line options */
 +        gmx::CommandLineParser(&options).skipUnknown(FF(PCA_NOEXIT_ON_ARGS))
 +            .parse(argc, argv);
 +        options.finish();
 +
 +        /* set program name, command line, and default values for output options */
 +        output_env_init(oenv, gmx::getProgramContext(),
 +                        (time_unit_t)(timeUnitManager.timeUnit() + 1), bView,
 +                        (xvg_format_t)(xvgFormat + 1), 0);
 +
 +        /* Set the nice level */
  #ifdef HAVE_UNISTD_H
  #ifndef GMX_NO_NICE
 -    /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
 -    if (nicelevel != 0 && !bExit)
 -    {
 -        static gmx_bool            nice_set   = FALSE; /* only set it once */
 -        static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
 -        tMPI_Thread_mutex_lock(&init_mutex);
 -        if (!nice_set)
 +        /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
 +        if (nicelevel != 0)
          {
 -            if (nice(nicelevel) == -1)
 +            static gmx_bool            nice_set   = FALSE; /* only set it once */
 +            static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
 +            tMPI_Thread_mutex_lock(&init_mutex);
 +            if (!nice_set)
              {
 -                /* Do nothing, but use the return value to avoid warnings. */
 +                if (nice(nicelevel) == -1)
 +                {
 +                    /* Do nothing, but use the return value to avoid warnings. */
 +                }
 +                nice_set = TRUE;
              }
 -            nice_set = TRUE;
 +            tMPI_Thread_mutex_unlock(&init_mutex);
          }
 -        tMPI_Thread_mutex_unlock(&init_mutex);
 -    }
  #endif
  #endif
  
 -    /* convert time options, must be done after printing! */
 +        timeUnitManager.scaleTimeOptions(&options);
  
 -    for (i = 0; i < npall; i++)
 -    {
 -        if (all_pa[i].type == etTIME && all_pa[i].bSet)
 +        /* Extract Time info from arguments */
 +        // TODO: Use OptionInfo objects instead of string constants
 +        if (FF(PCA_CAN_BEGIN) && options.isSet("b"))
          {
 -            *all_pa[i].u.r *= output_env_get_time_invfactor(*oenv);
 +            setTimeValue(TBEGIN, tbegin);
          }
 -    }
 -
 -    /* Extract Time info from arguments */
 -    if (FF(PCA_CAN_BEGIN) && opt2parg_bSet("-b", npall, all_pa))
 -    {
 -        setTimeValue(TBEGIN, opt2parg_real("-b", npall, all_pa));
 -    }
 -
 -    if (FF(PCA_CAN_END) && opt2parg_bSet("-e", npall, all_pa))
 -    {
 -        setTimeValue(TEND, opt2parg_real("-e", npall, all_pa));
 -    }
 -
 -    if (FF(PCA_CAN_DT) && opt2parg_bSet("-dt", npall, all_pa))
 -    {
 -        setTimeValue(TDELTA, opt2parg_real("-dt", npall, all_pa));
 -    }
 -
 -    /* clear memory */
 -    sfree(all_pa);
 -
 -    if (!FF(PCA_NOEXIT_ON_ARGS))
 -    {
 -        if (*argc > 1)
 +        if (FF(PCA_CAN_END) && options.isSet("e"))
 +        {
 +            setTimeValue(TEND, tend);
 +        }
 +        if (FF(PCA_CAN_DT) && options.isSet("dt"))
          {
 -            gmx_cmd(argv[1]);
 +            setTimeValue(TDELTA, tdelta);
          }
 +
 +        adapter.copyValues(!FF(PCA_NOT_READ_NODE));
 +
 +        return TRUE;
      }
 -    return !bExit;
 +    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
  #undef FF
  }
index 954139b6d09120fb059d4a363902c03de26581cf,fb1b076ee870d1be96996293b053e8f0fb511dea..039e36dcc06a76381c0d72f14169e73bb4885d33
  #endif
  
  #ifdef HAVE_SCHED_H
- #define _GNU_SOURCE
- #include <sched.h>
+ #  ifndef _GNU_SOURCE
+ #    define _GNU_SOURCE 1
+ #  endif
+ #  include <sched.h>
  #endif
  
  #include <stdio.h>
@@@ -465,7 -467,6 +467,7 @@@ cpuid_renumber_elements(int *data, int 
              }
          }
      }
 +    free(unique);
      return nunique;
  }
  
@@@ -556,7 -557,7 +558,7 @@@ cpuid_check_amd_x86(gmx_cpuid_
      /* Query APIC information on AMD */
      if (max_extfn >= 0x80000008)
      {
- #if (defined HAVE_SCHED_H && defined HAVE_SCHED_SETAFFINITY && defined HAVE_SYSCONF && defined __linux__)
+ #if (defined HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF && defined __linux__)
          /* Linux */
          unsigned int   i;
          cpu_set_t      cpuset, save_cpuset;
@@@ -670,7 -671,7 +672,7 @@@ cpuid_check_intel_x86(gmx_cpuid_
      if (max_stdfn >= 0xB)
      {
          /* Query x2 APIC information from cores */
- #if (defined HAVE_SCHED_H && defined HAVE_SCHED_SETAFFINITY && defined HAVE_SYSCONF && defined __linux__)
+ #if (defined HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF && defined __linux__)
          /* Linux */
          unsigned int   i;
          cpu_set_t      cpuset, save_cpuset;
index 17aa65ec069909a32333ee58d8db22d125f0c6ec,fb2064213ef6fe7bdd5de74ee8bdcb8d60c817b8..52898a0ed88afb23ca556448939a465ff67aa5b2
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
- #if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_GETAFFINITY)
- #define _GNU_SOURCE
- #include <sched.h>
- #include <sys/syscall.h>
+ #if defined(HAVE_SCHED_H)
+ #  ifndef _GNU_SOURCE
+ #    define _GNU_SOURCE 1
+ #  endif
+ #  include <sched.h>
+ #  include <sys/syscall.h>
  #endif
  #include <string.h>
  #include <errno.h>
  #include "md_logging.h"
  #include "gmx_thread_affinity.h"
  
 -#include "gmx_fatal.h"
 +#include "gromacs/utility/basenetwork.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/gmxomp.h"
 +#include "gromacs/utility/smalloc.h"
  
  static int
  get_thread_affinity_layout(FILE *fplog,
@@@ -370,53 -371,18 +372,53 @@@ gmx_set_thread_affinity(FIL
   * Note that this will only work on Linux as we use a GNU feature.
   */
  void
 -gmx_check_thread_affinity_set(FILE            gmx_unused *fplog,
 -                              const t_commrec gmx_unused *cr,
 -                              gmx_hw_opt_t    gmx_unused *hw_opt,
 -                              int             gmx_unused  nthreads_hw_avail,
 -                              gmx_bool        gmx_unused  bAfterOpenmpInit)
 +gmx_check_thread_affinity_set(FILE            *fplog,
 +                              const t_commrec *cr,
 +                              gmx_hw_opt_t    *hw_opt,
 +                              int  gmx_unused  nthreads_hw_avail,
 +                              gmx_bool         bAfterOpenmpInit)
  {
- #ifdef HAVE_SCHED_GETAFFINITY
+ #ifdef HAVE_SCHED_AFFINITY
      cpu_set_t mask_current;
      int       i, ret, cpu_count, cpu_set;
      gmx_bool  bAllSet;
 +#endif
  
      assert(hw_opt);
 +    if (!bAfterOpenmpInit)
 +    {
 +        /* Check for externally set OpenMP affinity and turn off internal
 +         * pinning if any is found. We need to do this check early to tell
 +         * thread-MPI whether it should do pinning when spawning threads.
 +         * TODO: the above no longer holds, we should move these checks later
 +         */
 +        if (hw_opt->thread_affinity != threadaffOFF)
 +        {
 +            char *message;
 +            if (!gmx_omp_check_thread_affinity(&message))
 +            {
 +                /* TODO: with -pin auto we should only warn when using all cores */
 +                md_print_warn(cr, fplog, "%s", message);
 +                sfree(message);
 +                hw_opt->thread_affinity = threadaffOFF;
 +            }
 +        }
 +
 +        /* With thread-MPI this is needed as pinning might get turned off,
 +         * which needs to be known before starting thread-MPI.
 +         * With thread-MPI hw_opt is processed here on the master rank
 +         * and passed to the other ranks later, so we only do this on master.
 +         */
 +        if (!SIMMASTER(cr))
 +        {
 +            return;
 +        }
 +#ifndef GMX_THREAD_MPI
 +        return;
 +#endif
 +    }
 +
 +#ifdef HAVE_SCHED_GETAFFINITY
      if (hw_opt->thread_affinity == threadaffOFF)
      {
          /* internal affinity setting is off, don't bother checking process affinity */
              fprintf(debug, "Default affinity mask found\n");
          }
      }
- #endif /* HAVE_SCHED_GETAFFINITY */
+ #endif /* HAVE_SCHED_AFFINITY */
  }
index f58a75b0019e60fd00d62ae2bc6e69bf9b4f011b,9d28fe0252d09912669d8dc38bc8bcd40a4f5382..322f0afed70aecc9533646e37d43e741e9d38097
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
  #include <ctype.h>
 -#include <stdlib.h>
  #include <limits.h>
 -#include "sysstuff.h"
 -#include "gromacs/utility/smalloc.h"
 +#include <stdlib.h>
 +
  #include "typedefs.h"
 -#include "physics.h"
 +#include "gromacs/math/units.h"
  #include "names.h"
 -#include "gmx_fatal.h"
  #include "macros.h"
 -#include "index.h"
 -#include "symtab.h"
 +#include "gromacs/topology/index.h"
  #include "gromacs/utility/cstringutil.h"
  #include "readinp.h"
  #include "warninp.h"
  #include "readir.h"
  #include "toputil.h"
 -#include "index.h"
  #include "network.h"
 -#include "vec.h"
 -#include "pbc.h"
 -#include "mtop_util.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/topology/mtop_util.h"
  #include "chargegroup.h"
  #include "inputrec.h"
  #include "calc_verletbuf.h"
  
 +#include "gromacs/topology/block.h"
 +#include "gromacs/topology/symtab.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/smalloc.h"
 +
  #define MAXPTR 254
  #define NOGID  255
  
@@@ -574,6 -575,8 +574,8 @@@ void check_ir(const char *mdparin, t_in
          CHECK(ir->nstlist <= 0);
          sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
          CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype));
+         sprintf(err_buf, "TPI does not work (yet) with the Verlet cut-off scheme");
+         CHECK(ir->cutoff_scheme == ecutsVERLET);
      }
  
      /* SHAKE / LINCS */
@@@ -1420,7 -1423,7 +1422,7 @@@ int str_nelem(const char *str, int maxp
      int   np = 0;
      char *copy0, *copy;
  
 -    copy0 = strdup(str);
 +    copy0 = gmx_strdup(str);
      copy  = copy0;
      ltrim(copy);
      while (*copy != '\0')
@@@ -1675,7 -1678,7 +1677,7 @@@ static void do_wall_params(t_inputrec *
          }
          for (i = 0; i < ir->nwall; i++)
          {
 -            opts->wall_atomtype[i] = strdup(names[i]);
 +            opts->wall_atomtype[i] = gmx_strdup(names[i]);
          }
  
          if (ir->wall_type == ewt93 || ir->wall_type == ewt104)
@@@ -2338,7 -2341,7 +2340,7 @@@ void get_ir(const char *mdparin, const 
      {
          if (ir->efep != efepNO)
          {
 -            opts->couple_moltype = strdup(is->couple_moltype);
 +            opts->couple_moltype = gmx_strdup(is->couple_moltype);
              if (opts->couple_lam0 == opts->couple_lam1)
              {
                  warning(wi, "The lambda=0 and lambda=1 states for coupling are identical");
@@@ -2930,7 -2933,7 +2932,7 @@@ static void decode_cos(char *s, t_cosin
      double  a, phi;
      int     i;
  
 -    t = strdup(s);
 +    t = gmx_strdup(s);
      trim(t);
  
      cosine->n   = 0;
@@@ -3111,7 -3114,7 +3113,7 @@@ static void make_swap_groups
  
  void make_IMD_group(t_IMD *IMDgroup, char *IMDgname, t_blocka *grps, char **gnames)
  {
 -    int      ig = -1, i;
 +    int      ig, i;
  
  
      ig            = search_string(IMDgname, grps->nr, gnames);
index 88a8cd3aecaa5e91b49dacc481a0852cd15543f0,da0b4a1c5dfdaa325f17293d320b7e66b22ca805..e2a26c73aa1d52a02d9e0f9b2c08bb1e744a808c
@@@ -41,7 -41,6 +41,7 @@@
  #include <stdio.h>
  #include "typedefs.h"
  #include "../math/gmxcomplex.h"
 +#include "../timing/wallcycle.h"
  #include "../timing/walltime_accounting.h"
  #include "../legacyheaders/network.h"
  
@@@ -153,6 -152,7 +153,7 @@@ gmx_pme_pp_t gmx_pme_pp_init(t_commrec 
  /* Initialize the PME-only side of the PME <-> PP communication */
  
  void gmx_pme_send_parameters(t_commrec *cr,
+                              const interaction_const_t *ic,
                               gmx_bool bFreeEnergy_q, gmx_bool bFreeEnergy_lj,
                               real *chargeA, real *chargeB,
                               real *sqrt_c6A, real *sqrt_c6B,
index b94aa743fc697c28a79b2e435c8c39ca4d434123,ae2a4f8098b696a91f846ef2e483abcfbc01b82f..09480a4be4b1b5017706b7c70cef6a6257c572a1
@@@ -33,7 -33,9 +33,7 @@@
   * the research papers on the package. Check out http://www.gromacs.org.
   */
  
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
  #include <stdio.h>
  #include <time.h>
  #include <assert.h>
  
  #include "typedefs.h"
 -#include "gromacs/utility/smalloc.h"
 -#include "gmx_fatal.h"
 -#include "gmx_fatal_collective.h"
 -#include "vec.h"
 +#include "network.h"
 +#include "gromacs/math/vec.h"
  #include "domdec.h"
  #include "domdec_network.h"
  #include "nrnb.h"
 -#include "pbc.h"
  #include "chargegroup.h"
  #include "constr.h"
  #include "mdatoms.h"
@@@ -57,7 -62,7 +57,7 @@@
  #include "mdrun.h"
  #include "nsgrid.h"
  #include "shellfc.h"
 -#include "mtop_util.h"
 +#include "gromacs/topology/mtop_util.h"
  #include "gmx_ga2la.h"
  #include "macros.h"
  #include "nbnxn_search.h"
  #include "gmx_omp_nthreads.h"
  #include "gpu_utils.h"
  
 -#include "gromacs/fileio/futil.h"
 +#include "gromacs/utility/futil.h"
  #include "gromacs/fileio/gmxfio.h"
  #include "gromacs/fileio/pdbio.h"
 +#include "gromacs/imd/imd.h"
 +#include "gromacs/mdlib/nb_verlet.h"
 +#include "gromacs/pbcutil/ishift.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/utility/basenetwork.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/gmxmpi.h"
 -#include "gromacs/swap/swapcoords.h"
  #include "gromacs/utility/qsort_threadsafe.h"
 -#include "gromacs/pulling/pull.h"
 -#include "gromacs/pulling/pull_rotation.h"
 -#include "gromacs/imd/imd.h"
 +#include "gromacs/utility/smalloc.h"
  
  #define DDRANK(dd, rank)    (rank)
  #define DDMASTERRANK(dd)   (dd->masterrank)
@@@ -1940,7 -1939,7 +1940,7 @@@ static void write_dd_grid_pdb(const cha
          snew(grid_r, 2*dd->nnodes);
      }
  
 -    dd_gather(dd, 2*sizeof(rvec), grid_s[0], DDMASTER(dd) ? grid_r[0] : NULL);
 +    dd_gather(dd, 2*sizeof(rvec), grid_s, DDMASTER(dd) ? grid_r : NULL);
  
      if (DDMASTER(dd))
      {
@@@ -9770,7 -9769,9 +9770,9 @@@ void dd_partition_system(FIL
      if (!(cr->duty & DUTY_PME))
      {
          /* Send the charges and/or c6/sigmas to our PME only node */
-         gmx_pme_send_parameters(cr, mdatoms->nChargePerturbed, mdatoms->nTypePerturbed,
+         gmx_pme_send_parameters(cr,
+                                 fr->ic,
+                                 mdatoms->nChargePerturbed, mdatoms->nTypePerturbed,
                                  mdatoms->chargeA, mdatoms->chargeB,
                                  mdatoms->sqrt_c6A, mdatoms->sqrt_c6B,
                                  mdatoms->sigmaA, mdatoms->sigmaB,
index a22020a633a0f5ad3fe357d84497ebc01364822b,c7382ab1ed14b969f5991a956eb87c51bfdc32ec..8069880ce220d14201c2550add48308fc8100d30
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
  #include <math.h>
  
 +#include "gromacs/math/vec.h"
 +#include "gromacs/topology/topology.h"
 +#include "gromacs/utility/fatalerror.h"
 +
  #include "perf_est.h"
 -#include "physics.h"
 -#include "vec.h"
 -#include "mtop_util.h"
  #include "types/commrec.h"
  #include "nbnxn_search.h"
  #include "nbnxn_consts.h"
  
 -
  /* Computational cost of bonded, non-bonded and PME calculations.
   * This will be machine dependent.
   * The numbers here are accurate for Intel Core2 and AMD Athlon 64
@@@ -99,6 -101,9 +99,9 @@@ int n_bonded_dx(gmx_mtop_t *mtop, gmx_b
       */
      ndx      = 0;
      ndx_excl = 0;
+ #if __ICC == 1400 || __ICL == 1400
+ #pragma novector /* Work-around for incorrect vectorization */
+ #endif
      for (mb = 0; mb < mtop->nmolblock; mb++)
      {
          molt = &mtop->moltype[mtop->molblock[mb].type];
diff --combined src/gromacs/mdlib/pme.c
index 924420ce0f3ad61516c82b2b172f58b9612330f6,532fa3a170e91d0fbca6bfd5b7d3179e16ca2edc..6e6a40e3b62bca21a158036c172f43c59a112334
   * /Erik 001109
   */
  
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
 +#include <assert.h>
 +#include <math.h>
  #include <stdio.h>
 +#include <stdlib.h>
  #include <string.h>
 -#include <math.h>
 -#include <assert.h>
 +
  #include "typedefs.h"
  #include "txtdump.h"
 -#include "vec.h"
 +#include "gromacs/math/vec.h"
  #include "gromacs/utility/smalloc.h"
  #include "coulomb.h"
 -#include "gmx_fatal.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "pme.h"
  #include "network.h"
 -#include "physics.h"
 +#include "gromacs/math/units.h"
  #include "nrnb.h"
  #include "macros.h"
  
 +#include "gromacs/legacyheaders/types/commrec.h"
  #include "gromacs/fft/parallel_3dfft.h"
 -#include "gromacs/fileio/futil.h"
 +#include "gromacs/utility/futil.h"
  #include "gromacs/fileio/pdbio.h"
  #include "gromacs/math/gmxcomplex.h"
  #include "gromacs/timing/cyclecounter.h"
@@@ -3988,7 -3987,8 +3988,8 @@@ reduce_threadgrid_overlap(gmx_pme_t pme
                      if (bCommY)
                      {
                          commbuf = commbuf_y;
-                         buf_my  = ty1 - offy;
+                         /* The y-size of the communication buffer is order-1 */
+                         buf_my  = pmegrid->order - 1;
                          if (bCommX)
                          {
                              /* We index commbuf modulo the local grid size */
index 9d9fa384572677207866cf1a269ab8945e6c3709,0aee97b824d4376cc52e0cc23e3f3d6cc44f174a..d245e92a30b1781a7da46ea98908c45525533962
@@@ -35,7 -35,9 +35,7 @@@
   * the research papers on the package. Check out http://www.gromacs.org.
   */
  
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
  
  #include <stdio.h>
@@@ -44,8 -46,8 +44,8 @@@
  #include "typedefs.h"
  #include "types/commrec.h"
  #include "gromacs/utility/smalloc.h"
 -#include "gmx_fatal.h"
 -#include "vec.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/math/vec.h"
  #include "pme.h"
  #include "network.h"
  #include "domdec.h"
@@@ -188,9 -190,11 +188,11 @@@ static void gmx_pme_send_coeffs_coords(
  
      if (debug)
      {
-         fprintf(debug, "PP rank %d sending to PME rank %d: %d%s%s\n",
+         fprintf(debug, "PP rank %d sending to PME rank %d: %d%s%s%s%s\n",
                  cr->sim_nodeid, dd->pme_nodeid, n,
                  flags & PP_PME_CHARGE ? " charges" : "",
+                 flags & PP_PME_SQRTC6 ? " sqrtC6" : "",
+                 flags & PP_PME_SIGMA  ? " sigma" : "",
                  flags & PP_PME_COORD  ? " coordinates" : "");
      }
  
  }
  
  void gmx_pme_send_parameters(t_commrec *cr,
+                              const interaction_const_t *ic,
                               gmx_bool bFreeEnergy_q, gmx_bool bFreeEnergy_lj,
                               real *chargeA, real *chargeB,
                               real *sqrt_c6A, real *sqrt_c6B,
  {
      int flags;
  
-     /* We always send the charges, even with only LJ- and no Coulomb-PME */
-     flags = PP_PME_CHARGE;
-     if (sqrt_c6A != NULL)
+     flags = 0;
+     if (EEL_PME(ic->eeltype))
      {
-         flags |= PP_PME_SQRTC6;
+         flags |= PP_PME_CHARGE;
      }
-     if (sigmaA != NULL)
+     if (EVDW_PME(ic->vdwtype))
      {
-         flags |= PP_PME_SIGMA;
+         flags |= (PP_PME_SQRTC6 | PP_PME_SIGMA);
      }
      if (bFreeEnergy_q || bFreeEnergy_lj)
      {
@@@ -559,9 -563,10 +561,10 @@@ int gmx_pme_recv_coeffs_coords(struct g
                          nat += pme_pp->nat[sender];
                          if (debug)
                          {
-                             fprintf(debug, "Received from PP rank %d: %d "
-                                     "charges\n",
-                                     pme_pp->node[sender], pme_pp->nat[sender]);
+                             fprintf(debug, "Received from PP rank %d: %d %s\n",
+                                     pme_pp->node[sender], pme_pp->nat[sender],
+                                     (q == eCommType_ChargeA ||
+                                      q == eCommType_ChargeB) ? "charges" : "params");
                          }
                      }
                  }
diff --combined src/gromacs/mdlib/tpi.c
index c19a5ef38c8ac71d13ab95d0af22291d0a2dc00a,8eaa88b740e1f3fd82bbb54c96ecbd1f41723d8e..d517174f1b353d40ca35673e8fc4269e3f7e170a
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
 +#include <math.h>
 +#include <stdlib.h>
  #include <string.h>
  #include <time.h>
 -#include <math.h>
 -#include "sysstuff.h"
 +
  #include "network.h"
  #include "gromacs/utility/smalloc.h"
  #include "nrnb.h"
 -#include "main.h"
  #include "chargegroup.h"
  #include "force.h"
  #include "macros.h"
  #include "names.h"
 -#include "gmx_fatal.h"
 +#include "gromacs/utility/fatalerror.h"
  #include "txtdump.h"
  #include "typedefs.h"
  #include "update.h"
  #include "constr.h"
 -#include "vec.h"
 +#include "gromacs/math/vec.h"
  #include "tgroup.h"
  #include "mdebin.h"
  #include "vsite.h"
  #include "mdrun.h"
  #include "domdec.h"
  #include "gromacs/random/random.h"
 -#include "physics.h"
 -#include "xvgr.h"
 +#include "gromacs/math/units.h"
 +#include "gromacs/fileio/xvgr.h"
  #include "mdatoms.h"
  #include "ns.h"
 -#include "mtop_util.h"
 +#include "gromacs/topology/mtop_util.h"
  #include "pme.h"
  #include "gromacs/gmxlib/conformation-utilities.h"
  
 +#include "gromacs/legacyheaders/types/commrec.h"
  #include "gromacs/fileio/confio.h"
  #include "gromacs/fileio/gmxfio.h"
  #include "gromacs/fileio/trxio.h"
@@@ -166,6 -167,11 +166,11 @@@ double do_tpi(FILE *fplog, t_commrec *c
      real bU_bin_limit      = 50;
      real bU_logV_bin_limit = bU_bin_limit + 10;
  
+     if (inputrec->cutoff_scheme == ecutsVERLET)
+     {
+         gmx_fatal(FARGS, "TPI does not work (yet) with the Verlet cut-off scheme");
+     }
      nnodes = cr->nnodes;
  
      top = gmx_mtop_generate_local_top(top_global, inputrec);
          snew(leg, 4+nener);
          e = 0;
          sprintf(str, "-kT log(<Ve\\S-\\betaU\\N>/<V>)");
 -        leg[e++] = strdup(str);
 +        leg[e++] = gmx_strdup(str);
          sprintf(str, "f. -kT log<e\\S-\\betaU\\N>");
 -        leg[e++] = strdup(str);
 +        leg[e++] = gmx_strdup(str);
          sprintf(str, "f. <e\\S-\\betaU\\N>");
 -        leg[e++] = strdup(str);
 +        leg[e++] = gmx_strdup(str);
          sprintf(str, "f. V");
 -        leg[e++] = strdup(str);
 +        leg[e++] = gmx_strdup(str);
          sprintf(str, "f. <Ue\\S-\\betaU\\N>");
 -        leg[e++] = strdup(str);
 +        leg[e++] = gmx_strdup(str);
          for (i = 0; i < ngid; i++)
          {
              sprintf(str, "f. <U\\sVdW %s\\Ne\\S-\\betaU\\N>",
                      *(groups->grpname[groups->grps[egcENER].nm_ind[i]]));
 -            leg[e++] = strdup(str);
 +            leg[e++] = gmx_strdup(str);
          }
          if (bDispCorr)
          {
              sprintf(str, "f. <U\\sdisp c\\Ne\\S-\\betaU\\N>");
 -            leg[e++] = strdup(str);
 +            leg[e++] = gmx_strdup(str);
          }
          if (bCharge)
          {
              {
                  sprintf(str, "f. <U\\sCoul %s\\Ne\\S-\\betaU\\N>",
                          *(groups->grpname[groups->grps[egcENER].nm_ind[i]]));
 -                leg[e++] = strdup(str);
 +                leg[e++] = gmx_strdup(str);
              }
              if (bRFExcl)
              {
                  sprintf(str, "f. <U\\sRF excl\\Ne\\S-\\betaU\\N>");
 -                leg[e++] = strdup(str);
 +                leg[e++] = gmx_strdup(str);
              }
              if (EEL_FULL(fr->eeltype))
              {
                  sprintf(str, "f. <U\\sCoul recip\\Ne\\S-\\betaU\\N>");
 -                leg[e++] = strdup(str);
 +                leg[e++] = gmx_strdup(str);
              }
          }
          xvgr_legend(fp_tpi, 4+nener, (const char**)leg, oenv);
index 00e7bd33a9a4a69b2a681b51445496b0717dce57,2a30686f39ccdd8077f28cec5799da392da6db6c..2eff412668fad91f94adff76cf904a1a45e2a6dd
@@@ -61,7 -61,7 +61,7 @@@ namespace gm
  namespace
  {
  
 -//! \addtogroup module_onlinehelp
 +//! \internal \addtogroup module_onlinehelp
  //! \{
  
  struct t_sandr
@@@ -357,7 -357,7 +357,7 @@@ class WrapperToVector : public WrapperI
  std::string toUpperCase(const std::string &text)
  {
      std::string result(text);
-     transform(result.begin(), result.end(), result.begin(), toupper);
+     std::transform(result.begin(), result.end(), result.begin(), toupper);
      return result;
  }
  
index a0a4a4023ba0651d14eae89940e5cdcf6eb60450,f5b58f0665236e1ff7253f1bc251f4cc924d6e09..8c06ea6887f20c904a2fc0fe292d9c883785ddcf
  #include "filenameoption.h"
  #include "filenameoptionstorage.h"
  
 +#include <cstring>
 +
  #include <string>
  #include <vector>
  
  #include "gromacs/fileio/filenm.h"
 -
 +#include "gromacs/options/filenameoptionmanager.h"
 +#include "gromacs/options/optionmanagercontainer.h"
  #include "gromacs/utility/arrayref.h"
 -#include "gromacs/utility/file.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/stringutil.h"
  
  namespace gmx
  namespace
  {
  
 -class FileTypeRegistry;
 -
  //! \addtogroup module_options
  //! \{
  
 -//! Shorthand for a list of file extensions.
 -typedef std::vector<const char *> ExtensionList;
 +/*! \brief
 + * Mapping from OptionFileType to a file type in filenm.h.
 + */
 +struct FileTypeMapping
 +{
 +    //! OptionFileType value to map.
 +    OptionFileType optionType;
 +    //! Corresponding file type from filenm.h.
 +    int            fileType;
 +};
 +
 +//! Mappings from OptionFileType to file types in filenm.h.
 +const FileTypeMapping c_fileTypeMapping[] =
 +{
 +    { eftTopology,    efTPS },
 +    { eftTrajectory,  efTRX },
 +    { eftPDB,         efPDB },
 +    { eftIndex,       efNDX },
 +    { eftPlot,        efXVG },
 +    { eftGenericData, efDAT }
 +};
  
  /********************************************************************
   * FileTypeHandler
   */
  
 -/*! \internal \brief
 +/*! \internal
 + * \brief
   * Handles a single file type known to FileNameOptionStorage.
 + *
 + * Methods in this class do not throw, except for a possible std::bad_alloc
 + * when constructing std::string return values.
   */
  class FileTypeHandler
  {
      public:
 -        //! Returns the list of extensions for this file type.
 -        const ExtensionList &extensions() const { return extensions_; }
 -
 -        //! Returns whether \p filename has a valid extension for this type.
 -        bool hasKnownExtension(const std::string &filename) const;
 -        //! Adds a default extension for this type to \p filename.
 -        std::string addExtension(const std::string &filename) const;
          /*! \brief
 -         * Adds an extension to \p filename if it results in an existing file.
 +         * Returns a handler for a single file type.
           *
 -         * Tries to add each extension for this file type to \p filename and
 -         * checks whether this results in an existing file.
 -         * The first match is returned.
 -         * Returns an empty string if no existing file is found.
 +         * \param[in] fileType  File type (from filenm.h) to use.
           */
 -        std::string findFileWithExtension(const std::string &filename) const;
 +        explicit FileTypeHandler(int fileType);
  
 -    private:
 -        //! Possible extensions for this file type.
 -        ExtensionList extensions_;
 +        //! Returns the number of acceptable extensions for this file type.
 +        int extensionCount() const;
 +        //! Returns the extension with the given index.
 +        const char *extension(int i) const;
  
 +        //! Returns whether \p fileType (from filenm.h) is accepted for this type.
 +        bool isValidType(int fileType) const;
 +
 +    private:
          /*! \brief
 -         * Needed for initialization; all initialization is handled by
 -         * FileTypeRegistry.
 +         * File type (from filenm.h) represented by this handler.
 +         *
 +         * -1 represents an unknown file type.
 +         */
 +        int        fileType_;
 +        //! Number of different extensions this type supports.
 +        int        extensionCount_;
 +        /*! \brief
 +         * List of simple file types that are included in this type.
 +         *
 +         * If `fileType_` represents a generic type in filenm.h, i.e., a type
 +         * that accepts multiple different types of files, then this is an
 +         * array of `extensionCount_` elements, each element specifying one
 +         * non-generic file type that this option accepts.
 +         * `NULL` for single-extension types.
           */
 -        friend class FileTypeRegistry;
 +        const int *genericTypes_;
  };
  
 -bool
 -FileTypeHandler::hasKnownExtension(const std::string &filename) const
 +FileTypeHandler::FileTypeHandler(int fileType)
 +    : fileType_(fileType), extensionCount_(0), genericTypes_(NULL)
  {
 -    for (size_t i = 0; i < extensions_.size(); ++i)
 +    if (fileType_ >= 0)
      {
 -        if (endsWith(filename, extensions_[i]))
 +        const int genericTypeCount = ftp2generic_count(fileType_);
 +        if (genericTypeCount > 0)
          {
 -            return true;
 +            extensionCount_ = genericTypeCount;
 +            genericTypes_   = ftp2generic_list(fileType_);
          }
 -    }
 -    return false;
 -}
 -
 -std::string
 -FileTypeHandler::addExtension(const std::string &filename) const
 -{
 -    if (extensions_.empty())
 -    {
 -        return filename;
 -    }
 -    return filename + extensions_[0];
 -}
 -
 -std::string
 -FileTypeHandler::findFileWithExtension(const std::string &filename) const
 -{
 -    for (size_t i = 0; i < extensions_.size(); ++i)
 -    {
 -        std::string testFilename(filename + extensions_[i]);
 -        if (File::exists(testFilename))
 +        else if (ftp2ext_with_dot(fileType_)[0] != '\0')
          {
 -            return testFilename;
 +            extensionCount_ = 1;
          }
      }
 -    return std::string();
  }
  
 -/********************************************************************
 - * FileTypeRegistry
 - */
 -
 -/*! \internal \brief
 - * Singleton for managing static file type info for FileNameOptionStorage.
 - */
 -class FileTypeRegistry
 -{
 -    public:
 -        //! Returns a singleton instance of this class.
 -        static const FileTypeRegistry &instance();
 -        //! Returns a handler for a single file type.
 -        const FileTypeHandler &
 -        handlerForType(OptionFileType type, int legacyType) const;
 -
 -    private:
 -        //! Initializes the file type registry.
 -        FileTypeRegistry();
 -
 -        //! Registers a file type that corresponds to a ftp in filenm.h.
 -        void registerType(int type, int ftp);
 -        //! Registers a file type with a single extension.
 -        void registerType(int type, const char *extension);
 -
 -        std::vector<FileTypeHandler> filetypes_;
 -};
 -
 -// static
 -const FileTypeRegistry &
 -FileTypeRegistry::instance()
 -{
 -    static FileTypeRegistry singleton;
 -    return singleton;
 -}
 -
 -const FileTypeHandler &
 -FileTypeRegistry::handlerForType(OptionFileType type, int legacyType) const
 +int FileTypeHandler::extensionCount() const
  {
 -    int index = type;
 -    if (type == eftUnknown && legacyType >= 0)
 -    {
 -        index = eftOptionFileType_NR + legacyType;
 -    }
 -    GMX_RELEASE_ASSERT(index >= 0 && static_cast<size_t>(index) < filetypes_.size(),
 -                       "Invalid file type");
 -    return filetypes_[index];
 +    return extensionCount_;
  }
  
 -FileTypeRegistry::FileTypeRegistry()
 +const char *FileTypeHandler::extension(int i) const
  {
 -    filetypes_.resize(eftOptionFileType_NR + efNR);
 -    registerType(eftTopology,    efTPS);
 -    registerType(eftTrajectory,  efTRX);
 -    registerType(eftPDB,         efPDB);
 -    registerType(eftIndex,       efNDX);
 -    registerType(eftPlot,        efXVG);
 -    registerType(eftGenericData, efDAT);
 -    for (int i = 0; i < efNR; ++i)
 +    GMX_ASSERT(i >= 0 && i < extensionCount_, "Invalid extension index");
 +    if (genericTypes_ != NULL)
      {
 -        registerType(eftOptionFileType_NR + i, i);
 +        return ftp2ext_with_dot(genericTypes_[i]);
      }
 +    return ftp2ext_with_dot(fileType_);
  }
  
 -void FileTypeRegistry::registerType(int type, int ftp)
 +bool
 +FileTypeHandler::isValidType(int fileType) const
  {
 -    GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
 -                       "Invalid file type");
 -    const int genericTypeCount = ftp2generic_count(ftp);
 -    if (genericTypeCount > 0)
 +    if (genericTypes_ != NULL)
      {
 -        const int *const genericTypes = ftp2generic_list(ftp);
 -        filetypes_[type].extensions_.clear();
 -        filetypes_[type].extensions_.reserve(genericTypeCount);
 -        for (int i = 0; i < genericTypeCount; ++i)
 +        for (int i = 0; i < extensionCount(); ++i)
          {
 -            filetypes_[type].extensions_.push_back(ftp2ext_with_dot(genericTypes[i]));
 +            if (fileType == genericTypes_[i])
 +            {
 +                return true;
 +            }
          }
 +        return false;
      }
      else
      {
 -        registerType(type, ftp2ext_with_dot(ftp));
 +        return fileType == fileType_;
      }
  }
  
 -void FileTypeRegistry::registerType(int type, const char *extension)
 -{
 -    GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
 -                       "Invalid file type");
 -    filetypes_[type].extensions_.assign(1, extension);
 -}
 -
 -/*! \brief
 - * Helper method to complete a file name provided to a file name option.
 - *
 - * \param[in] value      Value provided to the file name option.
 - * \param[in] filetype   File type for the option.
 - * \param[in] legacyType If \p filetype is eftUnknown, this gives the type as
 - *     an enum value from filenm.h.
 - * \param[in] bCompleteToExisting
 - *     Whether to check existing files when completing the extension.
 - * \returns   \p value with possible extension added.
 - */
 -std::string completeFileName(const std::string &value, OptionFileType filetype,
 -                             int legacyType, bool bCompleteToExisting)
 -{
 -    if (bCompleteToExisting && File::exists(value))
 -    {
 -        // TODO: This may not work as expected if the value is passed to a
 -        // function that uses fn2ftp() to determine the file type and the input
 -        // file has an unrecognized extension.
 -        return value;
 -    }
 -    const FileTypeRegistry &registry    = FileTypeRegistry::instance();
 -    const FileTypeHandler  &typeHandler = registry.handlerForType(filetype, legacyType);
 -    if (typeHandler.hasKnownExtension(value))
 -    {
 -        return value;
 -    }
 -    if (bCompleteToExisting)
 -    {
 -        std::string newValue = typeHandler.findFileWithExtension(value);
 -        if (!newValue.empty())
 -        {
 -            return newValue;
 -        }
 -    }
 -    return typeHandler.addExtension(value);
 -}
 -
  //! \}
  
  }   // namespace
   * FileNameOptionStorage
   */
  
 -FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
 -    : MyBase(settings), info_(this), filetype_(settings.filetype_),
 -      legacyType_(settings.legacyType_),
 +FileNameOptionStorage::FileNameOptionStorage(const FileNameOption  &settings,
 +                                             FileNameOptionManager *manager)
 +    : MyBase(settings), info_(this), manager_(manager), fileType_(-1),
        bRead_(settings.bRead_), bWrite_(settings.bWrite_),
        bLibrary_(settings.bLibrary_)
  {
 +    GMX_RELEASE_ASSERT(!hasFlag(efOption_MultipleTimes),
 +                       "allowMultiple() is not supported for file name options");
 +    if (settings.optionType_ == eftUnknown && settings.legacyType_ >= 0)
 +    {
 +        fileType_ = settings.legacyType_;
 +    }
 +    else
 +    {
 +        ConstArrayRef<FileTypeMapping>                 map(c_fileTypeMapping);
 +        ConstArrayRef<FileTypeMapping>::const_iterator i;
 +        for (i = map.begin(); i != map.end(); ++i)
 +        {
 +            if (i->optionType == settings.optionType_)
 +            {
 +                fileType_ = i->fileType;
 +                break;
 +            }
 +        }
 +    }
      if (settings.defaultBasename_ != NULL)
      {
 -        std::string defaultValue =
 -            completeFileName(settings.defaultBasename_, filetype_,
 -                             legacyType_, false);
 +        std::string defaultValue(settings.defaultBasename_);
 +        defaultValue.append(defaultExtension());
          setDefaultValueIfSet(defaultValue);
 -        if (isRequired())
 +        if (isRequired() || settings.bLegacyOptionalBehavior_)
          {
              setDefaultValue(defaultValue);
          }
  
  std::string FileNameOptionStorage::typeString() const
  {
 -    const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
 -    const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_, legacyType_);
 -    const ExtensionList          &extensions  = typeHandler.extensions();
 -    std::string                   result;
 -    ExtensionList::const_iterator i;
 -    int                           count = 0;
 -    for (i = extensions.begin(); count < 2 && i != extensions.end(); ++i, ++count)
 +    FileTypeHandler typeHandler(fileType_);
 +    std::string     result;
 +    int             count;
 +    for (count = 0; count < 2 && count < typeHandler.extensionCount(); ++count)
      {
 -        if (i != extensions.begin())
 +        if (count > 0)
          {
              result.append("/");
          }
 -        result.append(*i);
 +        result.append(typeHandler.extension(count));
      }
 -    if (i != extensions.end())
 +    if (count < typeHandler.extensionCount())
      {
          result.append("/...");
      }
      if (result.empty())
      {
 -        if (legacyType_ == efRND)
 +        if (isDirectoryOption())
          {
              result = "dir";
          }
  
  std::string FileNameOptionStorage::formatExtraDescription() const
  {
 -    const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
 -    const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_, legacyType_);
 -    const ExtensionList          &extensions  = typeHandler.extensions();
 -    std::string                   result;
 -    if (extensions.size() > 2)
 +    FileTypeHandler typeHandler(fileType_);
 +    std::string     result;
 +    if (typeHandler.extensionCount() > 2)
      {
          result.append(":");
 -        ExtensionList::const_iterator i;
 -        for (i = extensions.begin(); i != extensions.end(); ++i)
 +        for (int i = 0; i < typeHandler.extensionCount(); ++i)
          {
              result.append(" ");
 -            result.append((*i) + 1);
 +            // Skip the dot.
 +            result.append(typeHandler.extension(i) + 1);
          }
      }
      return result;
@@@ -289,141 -357,21 +289,141 @@@ std::string FileNameOptionStorage::form
  
  void FileNameOptionStorage::convertValue(const std::string &value)
  {
 -    bool bInput = isInputFile() || isInputOutputFile();
 -    addValue(completeFileName(value, filetype_, legacyType_, bInput));
 +    if (manager_ != NULL)
 +    {
 +        std::string processedValue = manager_->completeFileName(value, info_);
 +        if (!processedValue.empty())
 +        {
 +            // If the manager returns a value, use it without further checks,
 +            // except for sanity checking.
 +            if (!isDirectoryOption())
 +            {
 +                const int fileType = fn2ftp(processedValue.c_str());
 +                if (fileType == efNR)
 +                {
 +                    // If the manager returned an invalid file name, assume
 +                    // that it knows what it is doing.  But assert that it
 +                    // only does that for the only case that it is currently
 +                    // required for: VMD plugins.
 +                    GMX_ASSERT(isInputFile() && isTrajectoryOption(),
 +                               "Manager returned an invalid file name");
 +                }
 +                else
 +                {
 +                    GMX_ASSERT(isValidType(fileType),
 +                               "Manager returned an invalid file name");
 +                }
 +            }
 +            addValue(processedValue);
 +            return;
 +        }
 +    }
 +    // Currently, directory options are simple, and don't need any
 +    // special processing.
 +    // TODO: Consider splitting them into a separate DirectoryOption.
 +    if (isDirectoryOption())
 +    {
 +        addValue(value);
 +        return;
 +    }
 +    const int fileType = fn2ftp(value.c_str());
 +    if (fileType == efNR)
 +    {
 +        std::string message
 +            = formatString("File '%s' cannot be used by GROMACS because it "
 +                           "does not have a recognizable extension.\n"
 +                           "The following extensions are possible for this option:\n  %s",
 +                           value.c_str(), joinStrings(extensions(), ", ").c_str());
 +        GMX_THROW(InvalidInputError(message));
 +    }
 +    else if (!isValidType(fileType))
 +    {
 +        std::string message
 +            = formatString("File name '%s' cannot be used for this option.\n"
 +                           "Only the following extensions are possible:\n  %s",
 +                           value.c_str(), joinStrings(extensions(), ", ").c_str());
 +        GMX_THROW(InvalidInputError(message));
 +    }
 +    addValue(value);
 +}
 +
 +void FileNameOptionStorage::processAll()
 +{
 +    if (manager_ != NULL && hasFlag(efOption_HasDefaultValue))
 +    {
 +        ValueList &valueList = values();
 +        GMX_RELEASE_ASSERT(valueList.size() == 1,
 +                           "There should be only one default value");
 +        if (!valueList[0].empty())
 +        {
 +            const std::string &oldValue = valueList[0];
 +            GMX_ASSERT(endsWith(oldValue, defaultExtension()),
 +                       "Default value does not have the expected extension");
 +            const std::string  prefix
 +                = stripSuffixIfPresent(oldValue, defaultExtension());
 +            const std::string  newValue
 +                = manager_->completeDefaultFileName(prefix, info_);
 +            if (!newValue.empty() && newValue != oldValue)
 +            {
 +                GMX_ASSERT(isValidType(fn2ftp(newValue.c_str())),
 +                           "Manager returned an invalid default value");
 +                valueList[0] = newValue;
 +                refreshValues();
 +            }
 +        }
 +    }
  }
  
  bool FileNameOptionStorage::isDirectoryOption() const
  {
 -    return legacyType_ == efRND;
 +    return fileType_ == efRND;
 +}
 +
 +bool FileNameOptionStorage::isTrajectoryOption() const
 +{
 +    return fileType_ == efTRX;
  }
  
 -ConstArrayRef<const char *> FileNameOptionStorage::extensions() const
 +const char *FileNameOptionStorage::defaultExtension() const
  {
 -    const FileTypeRegistry &registry    = FileTypeRegistry::instance();
 -    const FileTypeHandler  &typeHandler = registry.handlerForType(filetype_, legacyType_);
 -    const ExtensionList    &extensions  = typeHandler.extensions();
 -    return constArrayRefFromVector<const char *>(extensions.begin(), extensions.end());
 +    FileTypeHandler typeHandler(fileType_);
 +    if (typeHandler.extensionCount() == 0)
 +    {
 +        return "";
 +    }
 +    return typeHandler.extension(0);
 +}
 +
 +std::vector<const char *> FileNameOptionStorage::extensions() const
 +{
 +    FileTypeHandler           typeHandler(fileType_);
 +    std::vector<const char *> result;
 +    result.reserve(typeHandler.extensionCount());
 +    for (int i = 0; i < typeHandler.extensionCount(); ++i)
 +    {
 +        result.push_back(typeHandler.extension(i));
 +    }
 +    return result;
 +}
 +
 +bool FileNameOptionStorage::isValidType(int fileType) const
 +{
 +    FileTypeHandler typeHandler(fileType_);
 +    return typeHandler.isValidType(fileType);
 +}
 +
 +ConstArrayRef<int> FileNameOptionStorage::fileTypes() const
 +{
 +    if (fileType_ < 0)
 +    {
 +        return ConstArrayRef<int>();
 +    }
 +    const int genericTypeCount = ftp2generic_count(fileType_);
 +    if (genericTypeCount > 0)
 +    {
-         return ConstArrayRef<int>(ftp2generic_list(fileType_), genericTypeCount);
++        return constArrayRefFromArray<int>(ftp2generic_list(fileType_), genericTypeCount);
 +    }
-     return ConstArrayRef<int>(&fileType_, 1);
++    return constArrayRefFromArray<int>(&fileType_, 1);
  }
  
  /********************************************************************
@@@ -465,39 -413,18 +465,39 @@@ bool FileNameOptionInfo::isDirectoryOpt
      return option().isDirectoryOption();
  }
  
 +bool FileNameOptionInfo::isTrajectoryOption() const
 +{
 +    return option().isTrajectoryOption();
 +}
 +
 +const char *FileNameOptionInfo::defaultExtension() const
 +{
 +    return option().defaultExtension();
 +}
 +
  FileNameOptionInfo::ExtensionList FileNameOptionInfo::extensions() const
  {
      return option().extensions();
  }
  
 +bool FileNameOptionInfo::isValidType(int fileType) const
 +{
 +    return option().isValidType(fileType);
 +}
 +
 +ConstArrayRef<int> FileNameOptionInfo::fileTypes() const
 +{
 +    return option().fileTypes();
 +}
 +
  /********************************************************************
   * FileNameOption
   */
  
 -AbstractOptionStoragePointer FileNameOption::createStorage() const
 +AbstractOptionStorage *
 +FileNameOption::createStorage(const OptionManagerContainer &managers) const
  {
 -    return AbstractOptionStoragePointer(new FileNameOptionStorage(*this));
 +    return new FileNameOptionStorage(*this, managers.get<FileNameOptionManager>());
  }
  
  } // namespace gmx
index b50c4dbd539c81fa63cc38077b496cf05976995f,24a66ddbda7538ff88f2e9f9e03c72ed9356bc5f..1e8f1cfdadfe46617dab5a475ca22e327ef95578
   */
  #include "gromacs/selection/nbsearch.h"
  
 -#include <math.h>
 +#include <cmath>
 +#include <cstring>
  
  #include <algorithm>
  #include <vector>
  
  #include "thread_mpi/mutex.h"
  
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/pbc.h"
 -#include "gromacs/legacyheaders/vec.h"
 +#include "gromacs/legacyheaders/names.h"
  
 +#include "gromacs/math/vec.h"
 +#include "gromacs/pbcutil/pbc.h"
  #include "gromacs/selection/position.h"
 +#include "gromacs/topology/block.h"
  #include "gromacs/utility/arrayref.h"
 +#include "gromacs/utility/exceptions.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/stringutil.h"
  
  namespace gmx
  {
@@@ -102,14 -98,10 +102,14 @@@ class AnalysisNeighborhoodSearchImp
           * Initializes the search with a given box and reference positions.
           *
           * \param[in]     mode      Search mode to use.
 +         * \param[in]     bXY       Whether to use 2D searching.
 +         * \param[in]     excls     Exclusions.
           * \param[in]     pbc       PBC information.
           * \param[in]     positions Set of reference positions.
           */
          void init(AnalysisNeighborhood::SearchMode     mode,
 +                  bool                                 bXY,
 +                  const t_blocka                      *excls,
                    const t_pbc                         *pbc,
                    const AnalysisNeighborhoodPositions &positions);
          PairSearchImplPointer getPairSearch();
           * \param[in]     pbc  Information about the box.
           * \returns  false if grid search is not suitable.
           */
 -        bool initGridCells(const t_pbc *pbc);
 +        bool initGridCells(const t_pbc &pbc);
          /*! \brief
           * Sets ua a search grid for a given box.
           *
           * \param[in]     pbc  Information about the box.
           * \returns  false if grid search is not suitable.
           */
 -        bool initGrid(const t_pbc *pbc);
 +        bool initGrid(const t_pbc &pbc);
          /*! \brief
           * Maps a point into a grid cell.
           *
          real                    cutoff_;
          //! The cutoff squared.
          real                    cutoff2_;
 +        //! Whether to do searching in XY plane only.
 +        bool                    bXY_;
  
          //! Number of reference points for the current frame.
          int                     nref_;
          //! Reference point positions.
          const rvec             *xref_;
 -        //! Reference position ids (NULL if not available).
 -        const int              *refid_;
 +        //! Reference position exclusion IDs.
 +        const int              *refExclusionIds_;
 +        //! Exclusions.
 +        const t_blocka         *excls_;
          //! PBC data.
 -        t_pbc                  *pbc_;
 -
 -        //! Number of excluded reference positions for current test particle.
 -        int                     nexcl_;
 -        //! Exclusions for current test particle.
 -        int                    *excl_;
 +        t_pbc                   pbc_;
  
          //! Whether grid searching is actually used for the current positions.
          bool                    bGrid_;
@@@ -215,9 -208,6 +215,9 @@@ class AnalysisNeighborhoodPairSearchImp
          explicit AnalysisNeighborhoodPairSearchImpl(const AnalysisNeighborhoodSearchImpl &search)
              : search_(search)
          {
 +            testExclusionIds_ = NULL;
 +            nexcl_            = 0;
 +            excl_             = NULL;
              clear_rvec(xtest_);
              clear_ivec(testcell_);
              reset(-1);
          const AnalysisNeighborhoodSearchImpl   &search_;
          //! Reference to the test positions.
          ConstArrayRef<rvec>                     testPositions_;
 +        //! Reference to the test exclusion indices.
 +        const int                              *testExclusionIds_;
 +        //! Number of excluded reference positions for current test particle.
 +        int                                     nexcl_;
 +        //! Exclusions for current test particle.
 +        const int                              *excl_;
          //! Index of the currently active test position in \p testPositions_.
          int                                     testIndex_;
          //! Stores test position during a pair loop.
          rvec                                    xtest_;
          //! Stores the previous returned position during a pair loop.
          int                                     previ_;
 +        //! Stores the pair distance corresponding to previ_;
 +        real                                    prevr2_;
          //! Stores the current exclusion index during loops.
          int                                     exclind_;
          //! Stores the test particle cell index during loops.
@@@ -283,12 -265,14 +283,12 @@@ AnalysisNeighborhoodSearchImpl::Analysi
          bTryGrid_   = false;
      }
      cutoff2_        = sqr(cutoff_);
 +    bXY_            = false;
  
 -    nref_           = 0;
 -    xref_           = NULL;
 -    refid_          = NULL;
 -    pbc_            = NULL;
 -
 -    nexcl_          = 0;
 -    excl_           = NULL;
 +    nref_            = 0;
 +    xref_            = NULL;
 +    refExclusionIds_ = NULL;
 +    std::memset(&pbc_, 0, sizeof(pbc_));
  
      bGrid_          = false;
  
@@@ -374,16 -358,16 +374,16 @@@ void AnalysisNeighborhoodSearchImpl::in
      }
  }
  
 -bool AnalysisNeighborhoodSearchImpl::initGridCells(const t_pbc *pbc)
 +bool AnalysisNeighborhoodSearchImpl::initGridCells(const t_pbc &pbc)
  {
      const real targetsize =
 -        pow(pbc->box[XX][XX] * pbc->box[YY][YY] * pbc->box[ZZ][ZZ]
 +        pow(pbc.box[XX][XX] * pbc.box[YY][YY] * pbc.box[ZZ][ZZ]
              * 10 / nref_, static_cast<real>(1./3.));
  
      int cellCount = 1;
      for (int dd = 0; dd < DIM; ++dd)
      {
 -        ncelldim_[dd] = static_cast<int>(pbc->box[dd][dd] / targetsize);
 +        ncelldim_[dd] = static_cast<int>(pbc.box[dd][dd] / targetsize);
          cellCount    *= ncelldim_[dd];
          if (ncelldim_[dd] < 3)
          {
      return true;
  }
  
 -bool AnalysisNeighborhoodSearchImpl::initGrid(const t_pbc *pbc)
 +bool AnalysisNeighborhoodSearchImpl::initGrid(const t_pbc &pbc)
  {
      /* TODO: This check could be improved. */
 -    if (0.5*pbc->max_cutoff2 < cutoff2_)
 +    if (0.5*pbc.max_cutoff2 < cutoff2_)
      {
          return false;
      }
          return false;
      }
  
 -    bTric_ = TRICLINIC(pbc->box);
 +    bTric_ = TRICLINIC(pbc.box);
      if (bTric_)
      {
          for (int dd = 0; dd < DIM; ++dd)
          {
 -            svmul(1.0 / ncelldim_[dd], pbc->box[dd], cellbox_[dd]);
 +            svmul(1.0 / ncelldim_[dd], pbc.box[dd], cellbox_[dd]);
          }
          m_inv_ur0(cellbox_, recipcell_);
      }
      {
          for (int dd = 0; dd < DIM; ++dd)
          {
 -            cellbox_[dd][dd]   = pbc->box[dd][dd] / ncelldim_[dd];
 +            cellbox_[dd][dd]   = pbc.box[dd][dd] / ncelldim_[dd];
              recipcell_[dd][dd] = 1.0 / cellbox_[dd][dd];
          }
      }
@@@ -480,43 -464,17 +480,43 @@@ void AnalysisNeighborhoodSearchImpl::ad
  
  void AnalysisNeighborhoodSearchImpl::init(
          AnalysisNeighborhood::SearchMode     mode,
 +        bool                                 bXY,
 +        const t_blocka                      *excls,
          const t_pbc                         *pbc,
          const AnalysisNeighborhoodPositions &positions)
  {
      GMX_RELEASE_ASSERT(positions.index_ == -1,
                         "Individual indexed positions not supported as reference");
 -    pbc_  = const_cast<t_pbc *>(pbc);
 +    bXY_ = bXY;
 +    if (bXY_ && pbc->ePBC != epbcNONE)
 +    {
 +        if (pbc->ePBC != epbcXY && pbc->ePBC != epbcXYZ)
 +        {
 +            std::string message =
 +                formatString("Computations in the XY plane are not supported with PBC type '%s'",
 +                             EPBC(pbc->ePBC));
 +            GMX_THROW(NotImplementedError(message));
 +        }
 +        if (std::fabs(pbc->box[ZZ][XX]) > GMX_REAL_EPS*pbc->box[ZZ][ZZ] ||
 +            std::fabs(pbc->box[ZZ][YY]) > GMX_REAL_EPS*pbc->box[ZZ][ZZ])
 +        {
 +            GMX_THROW(NotImplementedError("Computations in the XY plane are not supported when the last box vector is not parallel to the Z axis"));
 +        }
 +        set_pbc(&pbc_, epbcXY, const_cast<rvec *>(pbc->box));
 +    }
 +    else if (pbc != NULL)
 +    {
 +        pbc_  = *pbc;
 +    }
 +    else
 +    {
 +        pbc_.ePBC = epbcNONE;
 +    }
      nref_ = positions.count_;
      // TODO: Consider whether it would be possible to support grid searching in
      // more cases.
      if (mode == AnalysisNeighborhood::eSearchMode_Simple
 -        || pbc_ == NULL || pbc_->ePBC != epbcXYZ)
 +        || pbc_.ePBC != epbcXYZ)
      {
          bGrid_ = false;
      }
          {
              copy_rvec(positions.x_[i], xref_alloc_[i]);
          }
 -        put_atoms_in_triclinic_unitcell(ecenterTRIC, pbc_->box,
 +        put_atoms_in_triclinic_unitcell(ecenterTRIC, pbc_.box,
                                          nref_, xref_alloc_);
          for (int i = 0; i < nref_; ++i)
          {
      {
          xref_ = positions.x_;
      }
 -    // TODO: Once exclusions are supported, this may need to be initialized.
 -    refid_ = NULL;
 -}
 -
 -#if 0
 -/*! \brief
 - * Sets the exclusions for the next neighborhood search.
 - *
 - * \param[in,out] d     Neighborhood search data structure.
 - * \param[in]     nexcl Number of reference positions to exclude from next
 - *      search.
 - * \param[in]     excl  Indices of reference positions to exclude.
 - *
 - * The set exclusions remain in effect until the next call of this function.
 - */
 -void
 -gmx_ana_nbsearch_set_excl(gmx_ana_nbsearch_t *d, int nexcl, int excl[])
 -{
 -
 -    d->nexcl = nexcl;
 -    d->excl  = excl;
 +    excls_           = excls;
 +    refExclusionIds_ = NULL;
 +    if (excls != NULL)
 +    {
 +        // TODO: Check that the IDs are ascending, or remove the limitation.
 +        refExclusionIds_ = positions.exclusionIds_;
 +        GMX_RELEASE_ASSERT(refExclusionIds_ != NULL,
 +                           "Exclusion IDs must be set for reference positions "
 +                           "when exclusions are enabled");
 +    }
  }
 -#endif
  
  /********************************************************************
   * AnalysisNeighborhoodPairSearchImpl
@@@ -576,29 -546,12 +576,29 @@@ void AnalysisNeighborhoodPairSearchImpl
          copy_rvec(testPositions_[testIndex_], xtest_);
          if (search_.bGrid_)
          {
 -            put_atoms_in_triclinic_unitcell(ecenterTRIC, search_.pbc_->box,
 +            put_atoms_in_triclinic_unitcell(ecenterTRIC,
 +                                            const_cast<rvec *>(search_.pbc_.box),
                                              1, &xtest_);
              search_.mapPointToGridCell(xtest_, testcell_);
          }
 +        if (search_.excls_ != NULL)
 +        {
 +            const int exclIndex  = testExclusionIds_[testIndex];
 +            if (exclIndex < search_.excls_->nr)
 +            {
 +                const int startIndex = search_.excls_->index[exclIndex];
 +                nexcl_ = search_.excls_->index[exclIndex + 1] - startIndex;
 +                excl_  = &search_.excls_->a[startIndex];
 +            }
 +            else
 +            {
 +                nexcl_ = 0;
 +                excl_  = NULL;
 +            }
 +        }
      }
      previ_     = -1;
 +    prevr2_    = 0.0;
      exclind_   = 0;
      prevnbi_   = 0;
      prevcai_   = -1;
@@@ -615,17 -568,34 +615,17 @@@ void AnalysisNeighborhoodPairSearchImpl
  
  bool AnalysisNeighborhoodPairSearchImpl::isExcluded(int j)
  {
 -    if (exclind_ < search_.nexcl_)
 +    if (exclind_ < nexcl_)
      {
 -        if (search_.refid_)
 +        const int refId = search_.refExclusionIds_[j];
 +        while (exclind_ < nexcl_ && excl_[exclind_] < refId)
          {
 -            while (exclind_ < search_.nexcl_
 -                   && search_.excl_[exclind_] < search_.refid_[j])
 -            {
 -                ++exclind_;
 -            }
 -            if (exclind_ < search_.nexcl_
 -                && search_.refid_[j] == search_.excl_[exclind_])
 -            {
 -                ++exclind_;
 -                return true;
 -            }
 +            ++exclind_;
          }
 -        else
 +        if (exclind_ < nexcl_ && refId == excl_[exclind_])
          {
 -            while (search_.bGrid_ && exclind_ < search_.nexcl_
 -                   && search_.excl_[exclind_] < j)
 -            {
 -                ++exclind_;
 -            }
 -            if (search_.excl_[exclind_] == j)
 -            {
 -                ++exclind_;
 -                return true;
 -            }
 +            ++exclind_;
 +            return true;
          }
      }
      return false;
  void AnalysisNeighborhoodPairSearchImpl::startSearch(
          const AnalysisNeighborhoodPositions &positions)
  {
 +    testExclusionIds_ = positions.exclusionIds_;
 +    GMX_RELEASE_ASSERT(search_.excls_ == NULL || testExclusionIds_ != NULL,
 +                       "Exclusion IDs must be set when exclusions are enabled");
      if (positions.index_ < 0)
      {
-         testPositions_ = ConstArrayRef<rvec>(positions.x_, positions.count_);
+         testPositions_ = constArrayRefFromArray<rvec>(positions.x_, positions.count_);
          reset(0);
      }
      else
      {
          // Somewhat of a hack: setup the array such that only the last position
          // will be used.
-         testPositions_ = ConstArrayRef<rvec>(positions.x_, positions.index_ + 1);
+         testPositions_ = constArrayRefFromArray<rvec>(positions.x_, positions.index_ + 1);
          reset(positions.index_);
      }
  }
@@@ -658,8 -625,6 +658,8 @@@ bool AnalysisNeighborhoodPairSearchImpl
      {
          if (search_.bGrid_)
          {
 +            GMX_RELEASE_ASSERT(!search_.bXY_, "Grid-based XY searches not implemented");
 +
              int nbi = prevnbi_;
              int cai = prevcai_ + 1;
  
                          continue;
                      }
                      rvec       dx;
 -                    pbc_dx_aiuc(search_.pbc_, xtest_, search_.xref_[i], dx);
 +                    pbc_dx_aiuc(&search_.pbc_, xtest_, search_.xref_[i], dx);
                      const real r2 = norm2(dx);
                      if (r2 <= search_.cutoff2_)
                      {
                              prevnbi_ = nbi;
                              prevcai_ = cai;
                              previ_   = i;
 +                            prevr2_  = r2;
                              return true;
                          }
                      }
                      continue;
                  }
                  rvec dx;
 -                if (search_.pbc_)
 +                if (search_.pbc_.ePBC != epbcNONE)
                  {
 -                    pbc_dx(search_.pbc_, xtest_, search_.xref_[i], dx);
 +                    pbc_dx(&search_.pbc_, xtest_, search_.xref_[i], dx);
                  }
                  else
                  {
                      rvec_sub(xtest_, search_.xref_[i], dx);
                  }
 -                const real r2 = norm2(dx);
 +                const real r2
 +                    = search_.bXY_
 +                        ? dx[XX]*dx[XX] + dx[YY]*dx[YY]
 +                        : norm2(dx);
                  if (r2 <= search_.cutoff2_)
                  {
                      if (action(i, r2))
                      {
 -                        previ_ = i;
 +                        previ_  = i;
 +                        prevr2_ = r2;
                          return true;
                      }
                  }
@@@ -747,7 -707,7 +747,7 @@@ void AnalysisNeighborhoodPairSearchImpl
      }
      else
      {
 -        *pair = AnalysisNeighborhoodPair(previ_, testIndex_);
 +        *pair = AnalysisNeighborhoodPair(previ_, testIndex_, prevr2_);
      }
  }
  
@@@ -828,7 -788,7 +828,7 @@@ class AnalysisNeighborhood::Imp
          typedef AnalysisNeighborhoodSearch::ImplPointer SearchImplPointer;
          typedef std::vector<SearchImplPointer> SearchList;
  
 -        Impl() : cutoff_(0), mode_(eSearchMode_Automatic)
 +        Impl() : cutoff_(0), excls_(NULL), mode_(eSearchMode_Automatic), bXY_(false)
          {
          }
          ~Impl()
          tMPI::mutex             createSearchMutex_;
          SearchList              searchList_;
          real                    cutoff_;
 +        const t_blocka         *excls_;
          SearchMode              mode_;
 +        bool                    bXY_;
  };
  
  AnalysisNeighborhood::Impl::SearchImplPointer
@@@ -890,18 -848,6 +890,18 @@@ void AnalysisNeighborhood::setCutoff(re
      impl_->cutoff_ = cutoff;
  }
  
 +void AnalysisNeighborhood::setXYMode(bool bXY)
 +{
 +    impl_->bXY_ = bXY;
 +}
 +
 +void AnalysisNeighborhood::setTopologyExclusions(const t_blocka *excls)
 +{
 +    GMX_RELEASE_ASSERT(impl_->searchList_.empty(),
 +                       "Changing the exclusions after initSearch() not currently supported");
 +    impl_->excls_ = excls;
 +}
 +
  void AnalysisNeighborhood::setMode(SearchMode mode)
  {
      impl_->mode_ = mode;
@@@ -917,7 -863,7 +917,7 @@@ AnalysisNeighborhood::initSearch(const 
                                   const AnalysisNeighborhoodPositions &positions)
  {
      Impl::SearchImplPointer search(impl_->getSearch());
 -    search->init(mode(), pbc, positions);
 +    search->init(mode(), impl_->bXY_, impl_->excls_, pbc, positions);
      return AnalysisNeighborhoodSearch(search);
  }
  
@@@ -980,7 -926,7 +980,7 @@@ AnalysisNeighborhoodSearch::nearestPoin
      int           closestPoint = -1;
      MindistAction action(&closestPoint, &minDist2);
      (void)pairSearch.searchNext(action);
 -    return AnalysisNeighborhoodPair(closestPoint, 0);
 +    return AnalysisNeighborhoodPair(closestPoint, 0, minDist2);
  }
  
  AnalysisNeighborhoodPairSearch
index 892496298aebe0d090dd194163a0d8b602e9c4a7,786fb9912a469b2c16b772edfef5324b93ab1367..f317486fe2e9e9a3a95611e251b8a541a3845d57
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
 - * Copyright (c) 2009,2010,2011,2012,2013, by the GROMACS development team, led by
 + * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
   * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
   * and including many others, as listed in the AUTHORS file in the
   * top-level source directory and at http://www.gromacs.org.
@@@ -41,8 -41,6 +41,8 @@@
   */
  #include "selection.h"
  
 +#include "gromacs/topology/topology.h"
 +
  #include "nbsearch.h"
  #include "position.h"
  #include "selelem.h"
@@@ -247,13 -245,8 +247,13 @@@ SelectionData::restoreOriginalPositions
  
  Selection::operator AnalysisNeighborhoodPositions() const
  {
 -    return AnalysisNeighborhoodPositions(data().rawPositions_.x,
 -                                         data().rawPositions_.count());
 +    AnalysisNeighborhoodPositions pos(data().rawPositions_.x,
 +                                      data().rawPositions_.count());
 +    if (hasOnlyAtoms())
 +    {
 +        pos.exclusionIds(atomIndices());
 +    }
 +    return pos;
  }
  
  
@@@ -352,15 -345,9 +352,15 @@@ Selection::printDebugInfo(FILE *fp, in
  
  SelectionPosition::operator AnalysisNeighborhoodPositions() const
  {
 -    return AnalysisNeighborhoodPositions(sel_->rawPositions_.x,
 -                                         sel_->rawPositions_.count())
 -               .selectSingleFromArray(i_);
 +    AnalysisNeighborhoodPositions pos(sel_->rawPositions_.x,
 +                                      sel_->rawPositions_.count());
 +    if (sel_->hasOnlyAtoms())
 +    {
 +        // TODO: Move atomIndices() such that it can be reused here as well.
-         pos.exclusionIds(ConstArrayRef<int>(sel_->rawPositions_.m.mapb.a,
-                                             sel_->rawPositions_.m.mapb.nra));
++        pos.exclusionIds(constArrayRefFromArray<int>(sel_->rawPositions_.m.mapb.a,
++                                                     sel_->rawPositions_.m.mapb.nra));
 +    }
 +    return pos.selectSingleFromArray(i_);
  }
  
  } // namespace gmx
index 23440078337dee86c918735cd2c9b5fe81fa56b8,2f36c1ecf216453bbe9b575356eab376569c6fb3..30f060c5577ce72d813cd822faa0207d314d6885
  #include <string>
  #include <vector>
  
 -#include "../legacyheaders/typedefs.h"
 -
  #include "../utility/arrayref.h"
  #include "../utility/common.h"
  #include "../utility/gmxassert.h"
  
  #include "position.h"
 -#include "indexutil.h"
  #include "selectionenums.h"
  
 +struct t_topology;
 +
  namespace gmx
  {
  
@@@ -104,8 -105,6 +104,8 @@@ class SelectionDat
          bool isDynamic() const { return bDynamic_; }
          //! Returns the type of positions in the selection.
          e_index_t type() const { return rawPositions_.m.type; }
 +        //! Returns true if the selection only contains positions with a single atom each.
 +        bool hasOnlyAtoms() const { return type() == INDEX_ATOM; }
  
          //! Number of positions in the selection.
          int posCount() const { return rawPositions_.count(); }
@@@ -325,8 -324,6 +325,8 @@@ class Selectio
          bool isDynamic() const { return data().isDynamic(); }
          //! Returns the type of positions in the selection.
          e_index_t type() const { return data().type(); }
 +        //! Returns true if the selection only contains positions with a single atom each.
 +        bool hasOnlyAtoms() const { return data().hasOnlyAtoms(); }
  
          //! Total number of atoms in the selection.
          int atomCount() const
          //! Returns atom indices of all atoms in the selection.
          ConstArrayRef<int> atomIndices() const
          {
-             return ConstArrayRef<int>(sel_->rawPositions_.m.mapb.a,
-                                       sel_->rawPositions_.m.mapb.nra);
+             return constArrayRefFromArray(sel_->rawPositions_.m.mapb.a,
+                                           sel_->rawPositions_.m.mapb.nra);
          }
          //! Number of positions in the selection.
          int posCount() const { return data().posCount(); }
          //! Returns coordinates for this selection as a continuous array.
          ConstArrayRef<rvec> coordinates() const
          {
-             return ConstArrayRef<rvec>(data().rawPositions_.x, posCount());
+             return constArrayRefFromArray(data().rawPositions_.x, posCount());
          }
          //! Returns whether velocities are available for this selection.
          bool hasVelocities() const { return data().rawPositions_.v != NULL; }
          ConstArrayRef<rvec> velocities() const
          {
              GMX_ASSERT(hasVelocities(), "Velocities accessed, but unavailable");
-             return ConstArrayRef<rvec>(data().rawPositions_.v, posCount());
+             return constArrayRefFromArray(data().rawPositions_.v, posCount());
          }
          //! Returns whether forces are available for this selection.
          bool hasForces() const { return sel_->rawPositions_.f != NULL; }
          ConstArrayRef<rvec> forces() const
          {
              GMX_ASSERT(hasForces(), "Forces accessed, but unavailable");
-             return ConstArrayRef<rvec>(data().rawPositions_.f, posCount());
+             return constArrayRefFromArray(data().rawPositions_.f, posCount());
          }
          //! Returns masses for this selection as a continuous array.
          ConstArrayRef<real> masses() const
              // (and thus the masses and charges are fixed).
              GMX_ASSERT(data().posMass_.size() >= static_cast<size_t>(posCount()),
                         "Internal inconsistency");
-             return ConstArrayRef<real>(data().posMass_.begin(),
-                                        data().posMass_.begin() + posCount());
+             return constArrayRefFromVector<real>(data().posMass_.begin(),
+                                                  data().posMass_.begin() + posCount());
          }
          //! Returns charges for this selection as a continuous array.
          ConstArrayRef<real> charges() const
              // (and thus the masses and charges are fixed).
              GMX_ASSERT(data().posCharge_.size() >= static_cast<size_t>(posCount()),
                         "Internal inconsistency");
-             return ConstArrayRef<real>(data().posCharge_.begin(),
-                                        data().posCharge_.begin() + posCount());
+             return constArrayRefFromVector<real>(data().posCharge_.begin(),
+                                                  data().posCharge_.begin() + posCount());
          }
          /*! \brief
           * Returns reference IDs for this selection as a continuous array.
           */
          ConstArrayRef<int> refIds() const
          {
-             return ConstArrayRef<int>(data().rawPositions_.m.refid, posCount());
+             return constArrayRefFromArray(data().rawPositions_.m.refid, posCount());
          }
          /*! \brief
           * Returns mapped IDs for this selection as a continuous array.
           */
          ConstArrayRef<int> mappedIds() const
          {
-             return ConstArrayRef<int>(data().rawPositions_.m.mapid, posCount());
+             return constArrayRefFromArray(data().rawPositions_.m.mapid, posCount());
          }
  
          //! Returns whether the covered fraction can change between frames.
@@@ -657,7 -654,7 +657,7 @@@ class SelectionPositio
                  return ConstArrayRef<int>();
              }
              const int first = sel_->rawPositions_.m.mapb.index[i_];
-             return ConstArrayRef<int>(&atoms[first], atomCount());
+             return constArrayRefFromArray(&atoms[first], atomCount());
          }
          /*! \brief
           * Returns whether this position is selected in the current frame.
index 370235bd3160a5a15b3eddc9ec952a7750f1b10e,0ecf04b6e1af0738ea1d3b092d59a8c752412aff..0d7c0db1e86a7d03858c717af653ca727815b095
   * \author Teemu Murtola <teemu.murtola@gmail.com>
   * \ingroup module_selection
   */
 +#include "gromacs/selection/nbsearch.h"
 +
  #include <gtest/gtest.h>
  
  #include <cmath>
  
 +#include <algorithm>
  #include <limits>
 -#include <set>
 +#include <numeric>
  #include <vector>
  
 -#include "gromacs/legacyheaders/pbc.h"
 -#include "gromacs/legacyheaders/vec.h"
 -
 -#include "gromacs/selection/nbsearch.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/pbcutil/pbc.h"
  #include "gromacs/random/random.h"
 +#include "gromacs/topology/block.h"
  #include "gromacs/utility/smalloc.h"
  
  #include "testutils/testasserts.h"
@@@ -73,29 -71,6 +73,29 @@@ namespac
  class NeighborhoodSearchTestData
  {
      public:
 +        struct RefPair
 +        {
 +            RefPair(int refIndex, real distance)
 +                : refIndex(refIndex), distance(distance), bFound(false),
 +                  bExcluded(false)
 +            {
 +            }
 +
 +            bool operator<(const RefPair &other) const
 +            {
 +                return refIndex < other.refIndex;
 +            }
 +
 +            int                 refIndex;
 +            real                distance;
 +            // The variables below are state variables that are only used
 +            // during the actual testing after creating a copy of the reference
 +            // pair list, not as part of the reference data.
 +            // Simpler to have just a single structure for both purposes.
 +            bool                bFound;
 +            bool                bExcluded;
 +        };
 +
          struct TestPosition
          {
              TestPosition() : refMinDist(0.0), refNearestPoint(-1)
                  copy_rvec(x, this->x);
              }
  
 -            rvec                x;
 -            real                refMinDist;
 -            int                 refNearestPoint;
 -            std::set<int>       refPairs;
 +            rvec                 x;
 +            real                 refMinDist;
 +            int                  refNearestPoint;
 +            std::vector<RefPair> refPairs;
          };
 +
          typedef std::vector<TestPosition> TestPositionList;
  
          NeighborhoodSearchTestData(int seed, real cutoff);
          void generateRandomPosition(rvec x);
          void generateRandomRefPositions(int count);
          void generateRandomTestPositions(int count);
 -        void computeReferences(t_pbc *pbc);
 +        void computeReferences(t_pbc *pbc)
 +        {
 +            computeReferencesInternal(pbc, false);
 +        }
 +        void computeReferencesXY(t_pbc *pbc)
 +        {
 +            computeReferencesInternal(pbc, true);
 +        }
 +
 +        bool containsPair(int testIndex, const RefPair &pair) const
 +        {
 +            const std::vector<RefPair>          &refPairs = testPositions_[testIndex].refPairs;
 +            std::vector<RefPair>::const_iterator foundRefPair
 +                = std::lower_bound(refPairs.begin(), refPairs.end(), pair);
 +            if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex)
 +            {
 +                return false;
 +            }
 +            return true;
 +        }
  
          gmx_rng_t                        rng_;
          real                             cutoff_;
          TestPositionList                 testPositions_;
  
      private:
 +        void computeReferencesInternal(t_pbc *pbc, bool bXY);
 +
          mutable rvec                    *testPos_;
  };
  
 +//! Shorthand for a collection of reference pairs.
 +typedef std::vector<NeighborhoodSearchTestData::RefPair> RefPairList;
 +
  NeighborhoodSearchTestData::NeighborhoodSearchTestData(int seed, real cutoff)
      : rng_(NULL), cutoff_(cutoff), refPosCount_(0), refPos_(NULL), testPos_(NULL)
  {
@@@ -241,7 -191,7 +241,7 @@@ void NeighborhoodSearchTestData::genera
      }
  }
  
 -void NeighborhoodSearchTestData::computeReferences(t_pbc *pbc)
 +void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc *pbc, bool bXY)
  {
      real cutoff = cutoff_;
      if (cutoff <= 0)
              {
                  rvec_sub(i->x, refPos_[j], dx);
              }
 -            const real dist = norm(dx);
 +            // TODO: This may not work intuitively for 2D with the third box
 +            // vector not parallel to the Z axis, but neither does the actual
 +            // neighborhood search.
 +            const real dist =
 +                !bXY ? norm(dx) : sqrt(sqr(dx[XX]) + sqr(dx[YY]));
              if (dist < i->refMinDist)
              {
                  i->refMinDist      = dist;
              }
              if (dist <= cutoff)
              {
 -                i->refPairs.insert(j);
 +                RefPair pair(j, dist);
 +                GMX_RELEASE_ASSERT(i->refPairs.empty() || i->refPairs.back() < pair,
 +                                   "Reference pairs should be generated in sorted order");
 +                i->refPairs.push_back(pair);
              }
          }
      }
  }
  
-             return gmx::ConstArrayRef<int>(exclusionIds_.begin(),
-                                            exclusionIds_.begin() + refPosCount_);
 +/********************************************************************
 + * ExclusionsHelper
 + */
 +
 +class ExclusionsHelper
 +{
 +    public:
 +        static void markExcludedPairs(RefPairList *refPairs, int testIndex,
 +                                      const t_blocka *excls);
 +
 +        ExclusionsHelper(int refPosCount, int testPosCount);
 +
 +        void generateExclusions();
 +
 +        const t_blocka *exclusions() const { return &excls_; }
 +
 +        gmx::ConstArrayRef<int> refPosIds() const
 +        {
-             return gmx::ConstArrayRef<int>(exclusionIds_.begin(),
-                                            exclusionIds_.begin() + testPosCount_);
++            return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
++                                                     exclusionIds_.begin() + refPosCount_);
 +        }
 +        gmx::ConstArrayRef<int> testPosIds() const
 +        {
++            return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
++                                                     exclusionIds_.begin() + testPosCount_);
 +        }
 +
 +    private:
 +        int              refPosCount_;
 +        int              testPosCount_;
 +        std::vector<int> exclusionIds_;
 +        std::vector<int> exclsIndex_;
 +        std::vector<int> exclsAtoms_;
 +        t_blocka         excls_;
 +};
 +
 +// static
 +void ExclusionsHelper::markExcludedPairs(RefPairList *refPairs, int testIndex,
 +                                         const t_blocka *excls)
 +{
 +    int count = 0;
 +    for (int i = excls->index[testIndex]; i < excls->index[testIndex + 1]; ++i)
 +    {
 +        const int                           excludedIndex = excls->a[i];
 +        NeighborhoodSearchTestData::RefPair searchPair(excludedIndex, 0.0);
 +        RefPairList::iterator               excludedRefPair
 +            = std::lower_bound(refPairs->begin(), refPairs->end(), searchPair);
 +        if (excludedRefPair != refPairs->end()
 +            && excludedRefPair->refIndex == excludedIndex)
 +        {
 +            excludedRefPair->bFound    = true;
 +            excludedRefPair->bExcluded = true;
 +            ++count;
 +        }
 +    }
 +}
 +
 +ExclusionsHelper::ExclusionsHelper(int refPosCount, int testPosCount)
 +    : refPosCount_(refPosCount), testPosCount_(testPosCount)
 +{
 +    // Generate an array of 0, 1, 2, ...
 +    // TODO: Make the tests work also with non-trivial exclusion IDs,
 +    // and test that.
 +    exclusionIds_.resize(std::max(refPosCount, testPosCount), 1);
 +    exclusionIds_[0] = 0;
 +    std::partial_sum(exclusionIds_.begin(), exclusionIds_.end(),
 +                     exclusionIds_.begin());
 +
 +    excls_.nr           = 0;
 +    excls_.index        = NULL;
 +    excls_.nra          = 0;
 +    excls_.a            = NULL;
 +    excls_.nalloc_index = 0;
 +    excls_.nalloc_a     = 0;
 +}
 +
 +void ExclusionsHelper::generateExclusions()
 +{
 +    // TODO: Consider a better set of test data, where the density of the
 +    // particles would be higher, or where the exclusions would not be random,
 +    // to make a higher percentage of the exclusions to actually be within the
 +    // cutoff.
 +    exclsIndex_.reserve(testPosCount_ + 1);
 +    exclsAtoms_.reserve(testPosCount_ * 20);
 +    exclsIndex_.push_back(0);
 +    for (int i = 0; i < testPosCount_; ++i)
 +    {
 +        for (int j = 0; j < 20; ++j)
 +        {
 +            exclsAtoms_.push_back(i + j*3);
 +        }
 +        exclsIndex_.push_back(exclsAtoms_.size());
 +    }
 +    excls_.nr    = exclsIndex_.size();
 +    excls_.index = &exclsIndex_[0];
 +    excls_.nra   = exclsAtoms_.size();
 +    excls_.a     = &exclsAtoms_[0];
 +}
 +
  /********************************************************************
   * NeighborhoodSearchTest
   */
@@@ -400,10 -244,6 +400,10 @@@ class NeighborhoodSearchTest : public :
                                const NeighborhoodSearchTestData &data);
          void testPairSearch(gmx::AnalysisNeighborhoodSearch  *search,
                              const NeighborhoodSearchTestData &data);
 +        void testPairSearchFull(gmx::AnalysisNeighborhoodSearch          *search,
 +                                const NeighborhoodSearchTestData         &data,
 +                                const gmx::AnalysisNeighborhoodPositions &pos,
 +                                const t_blocka                           *excls);
  
          gmx::AnalysisNeighborhood        nb_;
  };
@@@ -446,8 -286,6 +446,8 @@@ void NeighborhoodSearchTest::testNeares
          {
              EXPECT_EQ(i->refNearestPoint, pair.refIndex());
              EXPECT_EQ(0, pair.testIndex());
 +            EXPECT_REAL_EQ_TOL(i->refMinDist, sqrt(pair.distance2()),
 +                               gmx::test::ulpTolerance(64));
          }
          else
          {
      }
  }
  
 +/*! \brief
 + * Helper function to check that all expected pairs were found.
 + */
 +static void checkAllPairsFound(const RefPairList &refPairs)
 +{
 +    // This could be elegantly expressed with Google Mock matchers, but that
 +    // has a significant effect on the runtime of the tests...
 +    for (RefPairList::const_iterator i = refPairs.begin(); i != refPairs.end(); ++i)
 +    {
 +        if (!i->bFound)
 +        {
 +            ADD_FAILURE()
 +            << "Some pairs within the cutoff were not found.";
 +            break;
 +        }
 +    }
 +}
 +
  void NeighborhoodSearchTest::testPairSearch(
          gmx::AnalysisNeighborhoodSearch  *search,
          const NeighborhoodSearchTestData &data)
  {
 -    NeighborhoodSearchTestData::TestPositionList::const_iterator i;
 -    for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
 +    testPairSearchFull(search, data, data.testPositions(), NULL);
 +}
 +
 +void NeighborhoodSearchTest::testPairSearchFull(
 +        gmx::AnalysisNeighborhoodSearch          *search,
 +        const NeighborhoodSearchTestData         &data,
 +        const gmx::AnalysisNeighborhoodPositions &pos,
 +        const t_blocka                           *excls)
 +{
 +    // TODO: Some parts of this code do not work properly if pos does not
 +    // contain all the test positions.
 +    std::set<int> remainingTestPositions;
 +    for (size_t i = 0; i < data.testPositions_.size(); ++i)
      {
 -        std::set<int> checkSet                         = i->refPairs;
 -        gmx::AnalysisNeighborhoodPairSearch pairSearch =
 -            search->startPairSearch(i->x);
 -        gmx::AnalysisNeighborhoodPair       pair;
 -        while (pairSearch.findNextPair(&pair))
 +        remainingTestPositions.insert(i);
 +    }
 +    gmx::AnalysisNeighborhoodPairSearch pairSearch
 +        = search->startPairSearch(pos);
 +    gmx::AnalysisNeighborhoodPair       pair;
 +    // TODO: There is an ordering assumption here that may break in the future:
 +    // all pairs for a test position are assumed to be returned consencutively.
 +    RefPairList refPairs;
 +    int         prevTestPos = -1;
 +    while (pairSearch.findNextPair(&pair))
 +    {
 +        if (pair.testIndex() != prevTestPos)
          {
 -            EXPECT_EQ(0, pair.testIndex());
 -            if (checkSet.erase(pair.refIndex()) == 0)
 +            if (prevTestPos != -1)
 +            {
 +                checkAllPairsFound(refPairs);
 +            }
 +            const int testIndex = pair.testIndex();
 +            if (remainingTestPositions.count(testIndex) == 0)
              {
 -                // TODO: Check whether the same pair was returned more than
 -                // once and give a better error message if so.
                  ADD_FAILURE()
 -                << "Expected: Position " << pair.refIndex()
 -                << " is within cutoff.\n"
 -                << "  Actual: It is not.";
 +                << "Pairs for test position " << testIndex
 +                << " are returned more than once.";
              }
 +            remainingTestPositions.erase(testIndex);
 +            refPairs = data.testPositions_[testIndex].refPairs;
 +            if (excls != NULL)
 +            {
 +                ExclusionsHelper::markExcludedPairs(&refPairs, testIndex, excls);
 +            }
 +            prevTestPos = testIndex;
 +        }
 +
 +        NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(),
 +                                                       sqrt(pair.distance2()));
 +        RefPairList::iterator               foundRefPair
 +            = std::lower_bound(refPairs.begin(), refPairs.end(), searchPair);
 +        if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex())
 +        {
 +            ADD_FAILURE()
 +            << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
 +            << pair.testIndex() << ") is not within the cutoff.\n"
 +            << "  Actual: It is returned.";
 +        }
 +        else if (foundRefPair->bExcluded)
 +        {
 +            ADD_FAILURE()
 +            << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
 +            << pair.testIndex() << ") is excluded from the search.\n"
 +            << "  Actual: It is returned.";
 +        }
 +        else if (foundRefPair->bFound)
 +        {
 +            ADD_FAILURE()
 +            << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
 +            << pair.testIndex() << ") is returned only once.\n"
 +            << "  Actual: It is returned multiple times.";
 +        }
 +        else
 +        {
 +            foundRefPair->bFound = true;
 +            EXPECT_REAL_EQ_TOL(foundRefPair->distance, searchPair.distance,
 +                               gmx::test::ulpTolerance(64))
 +            << "Distance computed by the neighborhood search does not match.";
 +        }
 +    }
 +    checkAllPairsFound(refPairs);
 +    for (std::set<int>::const_iterator i = remainingTestPositions.begin();
 +         i != remainingTestPositions.end(); ++i)
 +    {
 +        if (!data.testPositions_[*i].refPairs.empty())
 +        {
 +            ADD_FAILURE()
 +            << "Expected: Pairs would be returned for test position " << *i << ".\n"
 +            << "  Actual: None were returned.";
 +            break;
          }
 -        EXPECT_TRUE(checkSet.empty()) << "Some positions were not returned by the pair search.";
      }
  }
  
@@@ -626,32 -376,6 +626,32 @@@ class RandomBoxFullPBCDat
          NeighborhoodSearchTestData data_;
  };
  
 +class RandomBoxXYFullPBCData
 +{
 +    public:
 +        static const NeighborhoodSearchTestData &get()
 +        {
 +            static RandomBoxXYFullPBCData singleton;
 +            return singleton.data_;
 +        }
 +
 +        RandomBoxXYFullPBCData() : data_(54321, 1.0)
 +        {
 +            data_.box_[XX][XX] = 10.0;
 +            data_.box_[YY][YY] = 5.0;
 +            data_.box_[ZZ][ZZ] = 7.0;
 +            // TODO: Consider whether manually picking some positions would give better
 +            // test coverage.
 +            data_.generateRandomRefPositions(1000);
 +            data_.generateRandomTestPositions(100);
 +            set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
 +            data_.computeReferencesXY(&data_.pbc_);
 +        }
 +
 +    private:
 +        NeighborhoodSearchTestData data_;
 +};
 +
  class RandomTriclinicFullPBCData
  {
      public:
@@@ -773,24 -497,6 +773,24 @@@ TEST_F(NeighborhoodSearchTest, GridSear
      testPairSearch(&search, data);
  }
  
 +TEST_F(NeighborhoodSearchTest, GridSearchXYBox)
 +{
 +    const NeighborhoodSearchTestData &data = RandomBoxXYFullPBCData::get();
 +
 +    nb_.setCutoff(data.cutoff_);
 +    nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
 +    nb_.setXYMode(true);
 +    gmx::AnalysisNeighborhoodSearch search =
 +        nb_.initSearch(&data.pbc_, data.refPositions());
 +    // Currently, grid searching not supported with XY.
 +    //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
 +
 +    testIsWithin(&search, data);
 +    testMinimumDistance(&search, data);
 +    testNearestPoint(&search, data);
 +    testPairSearch(&search, data);
 +}
 +
  TEST_F(NeighborhoodSearchTest, HandlesConcurrentSearches)
  {
      const NeighborhoodSearchTestData &data = TrivialTestData::get();
      testPairSearch(&search2, data);
  
      gmx::AnalysisNeighborhoodPair pair;
 -    pairSearch1.findNextPair(&pair);
 +    ASSERT_TRUE(pairSearch1.findNextPair(&pair))
 +    << "Test data did not contain any pairs for position 0 (problem in the test).";
      EXPECT_EQ(0, pair.testIndex());
 -    EXPECT_TRUE(data.testPositions_[0].refPairs.count(pair.refIndex()) == 1);
 +    {
 +        NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
 +        EXPECT_TRUE(data.containsPair(0, searchPair));
 +    }
  
 -    pairSearch2.findNextPair(&pair);
 +    ASSERT_TRUE(pairSearch2.findNextPair(&pair))
 +    << "Test data did not contain any pairs for position 1 (problem in the test).";
      EXPECT_EQ(1, pair.testIndex());
 -    EXPECT_TRUE(data.testPositions_[1].refPairs.count(pair.refIndex()) == 1);
 +    {
 +        NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
 +        EXPECT_TRUE(data.containsPair(1, searchPair));
 +    }
  }
  
  TEST_F(NeighborhoodSearchTest, HandlesSkippingPairs)
              ++currentIndex;
          }
          EXPECT_EQ(currentIndex, pair.testIndex());
 -        EXPECT_TRUE(data.testPositions_[currentIndex].refPairs.count(pair.refIndex()) == 1);
 +        NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
 +        EXPECT_TRUE(data.containsPair(currentIndex, searchPair));
          pairSearch.skipRemainingPairsForTestPosition();
          ++currentIndex;
      }
  }
  
 +TEST_F(NeighborhoodSearchTest, SimpleSearchExclusions)
 +{
 +    const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
 +
 +    ExclusionsHelper                  helper(data.refPosCount_, data.testPositions_.size());
 +    helper.generateExclusions();
 +
 +    nb_.setCutoff(data.cutoff_);
 +    nb_.setTopologyExclusions(helper.exclusions());
 +    nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
 +    gmx::AnalysisNeighborhoodSearch search =
 +        nb_.initSearch(&data.pbc_,
 +                       data.refPositions().exclusionIds(helper.refPosIds()));
 +    ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
 +
 +    testPairSearchFull(&search, data,
 +                       data.testPositions().exclusionIds(helper.testPosIds()),
 +                       helper.exclusions());
 +}
 +
 +TEST_F(NeighborhoodSearchTest, GridSearchExclusions)
 +{
 +    const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
 +
 +    ExclusionsHelper                  helper(data.refPosCount_, data.testPositions_.size());
 +    helper.generateExclusions();
 +
 +    nb_.setCutoff(data.cutoff_);
 +    nb_.setTopologyExclusions(helper.exclusions());
 +    nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
 +    gmx::AnalysisNeighborhoodSearch search =
 +        nb_.initSearch(&data.pbc_,
 +                       data.refPositions().exclusionIds(helper.refPosIds()));
 +    ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
 +
 +    testPairSearchFull(&search, data,
 +                       data.testPositions().exclusionIds(helper.testPosIds()),
 +                       helper.exclusions());
 +}
 +
  } // namespace
index b4959ceba7377e8397d245ea8210e4acb115438b,b892070c45a7a6b7c6a1f1d4bb1c9ed004435012..a3213c99a5397be9fefabeedb793ede8a4f807c9
@@@ -32,7 -32,9 +32,7 @@@
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "config.h"
  
  #include <vector>
  #include "gromacs/math/utilities.h"
@@@ -279,6 -281,23 +279,23 @@@ TEST_F(SimdMathTest, gmxSimdExp2R
  {
      setRange(-100, 100);
      GMX_EXPECT_SIMD_FUNC_NEAR(ref_exp2, gmx_simd_exp2_r);
+     // We do not care about the SIMD implementation getting denormal values right,
+     // but they must be clamped to zero rather than producing garbage.
+     // Check by setting the absolute tolerance to machine precision.
+     setAbsTol(GMX_REAL_EPS);
+     // First two values will have denormal results in single, third value in double too.
+     GMX_EXPECT_SIMD_REAL_NEAR(setSimdRealFrom3R(ref_exp2(-150.0), ref_exp2(-300.0), ref_exp2(-1050.0)),
+                               gmx_simd_exp2_r(setSimdRealFrom3R(-150.0, -300.0, -1050.0)));
+     // Reset absolute tolerance to enforce ULP checking
+     setAbsTol(0.0);
+     // Make sure that underflowing values are set to zero.
+     // First two values underflow in single, third value in double too.
+     GMX_EXPECT_SIMD_REAL_NEAR(setSimdRealFrom3R(ref_exp2(-200.0), ref_exp2(-600.0), ref_exp2(-1500.0)),
+                               gmx_simd_exp2_r(setSimdRealFrom3R(-200.0, -600.0, -1500.0)));
  }
  #endif
  
@@@ -292,6 -311,22 +309,22 @@@ TEST_F(SimdMathTest, gmxSimdExpR
  {
      setRange(-75, 75);
      GMX_EXPECT_SIMD_FUNC_NEAR(ref_exp, gmx_simd_exp_r);
+     // We do not care about the SIMD implementation getting denormal values right,
+     // but they must be clamped to zero rather than producing garbage.
+     // Check by setting the absolute tolerance to machine precision.
+     setAbsTol(GMX_REAL_EPS);
+     // First two values will have denormal results in single, third value in double too.
+     GMX_EXPECT_SIMD_REAL_NEAR(setSimdRealFrom3R(ref_exp(-90.0), ref_exp(-100.0), ref_exp(-725.0)),
+                               gmx_simd_exp_r(setSimdRealFrom3R(-90.0, -100.0, -725.0)));
+     // Reset absolute tolerance to enforce ULP checking
+     setAbsTol(0.0);
+     // Make sure that underflowing values are set to zero.
+     // First two values underflow in single, third value in double too.
+     GMX_EXPECT_SIMD_REAL_NEAR(setSimdRealFrom3R(ref_exp(-150.0), ref_exp(-300.0), ref_exp(-800.0)),
+                               gmx_simd_exp_r(setSimdRealFrom3R(-150.0, -300.0, -800.0)));
  }
  
  /*! \brief Function wrapper for erf(x), with argument/return in default Gromacs precision.
diff --combined tests/CMakeLists.txt
index bcf188a6621a2b05ee543004c2bb995ae5eeead1,fb611ffd205dda1b6af090c0c0d51e4056caaacd..3bd404724f3ba8cd9b703fe6ef00fd71afbff6b1
@@@ -38,15 -38,15 +38,15 @@@ option(REGRESSIONTEST_DOWNLOA
      "Automatically download regressiontests. Tests can be run with ctest." OFF)
  
  if(REGRESSIONTEST_DOWNLOAD)
 -    if("${PROJECT_VERSION}" MATCHES "-dev")
 -      set(REGRESSIONTEST_URL http://gerrit.gromacs.org/snapshot/${REGRESSIONTEST_BRANCH})
 -      set(REGRESSIONTEST_PATH "${CMAKE_CURRENT_BINARY_DIR}/regressiontests"
 -           CACHE PATH "Path to auto-downloaded regressiontests" FORCE)
 +    if (NOT SOURCE_IS_SOURCE_DISTRIBUTION)
 +        set(REGRESSIONTEST_URL http://gerrit.gromacs.org/snapshot/${REGRESSIONTEST_BRANCH})
 +        set(REGRESSIONTEST_PATH "${CMAKE_CURRENT_BINARY_DIR}/regressiontests"
 +            CACHE PATH "Path to auto-downloaded regressiontests" FORCE)
      else()
          set(REGRESSIONTEST_URL http://gerrit.gromacs.org/download/regressiontests-${REGRESSIONTEST_VERSION}.tar.gz)
          set(REGRESSIONTEST_PATH
 -           "${CMAKE_CURRENT_BINARY_DIR}/regressiontests-${REGRESSIONTEST_VERSION}"
 -           CACHE PATH "Path to auto-downloaded regressiontests" FORCE)
 +            "${CMAKE_CURRENT_BINARY_DIR}/regressiontests-${REGRESSIONTEST_VERSION}"
 +            CACHE PATH "Path to auto-downloaded regressiontests" FORCE)
      endif()
      set(REGRESSIONTEST_FILE "${CMAKE_CURRENT_BINARY_DIR}/regressiontests.tgz")
      message("Downloading: ${REGRESSIONTEST_URL}")
@@@ -60,6 -60,12 +60,12 @@@ status_code: ${status_code
  status_string: ${status_string}
  log: ${log}")
      endif()
 -    if(NOT "${PROJECT_VERSION}" MATCHES "-dev")
++    if (SOURCE_IS_SOURCE_DISTRIBUTION)
+         file(MD5 ${REGRESSIONTEST_FILE} COMPUTED_MD5SUM)
+         if(NOT ${REGRESSIONTEST_MD5SUM} STREQUAL ${COMPUTED_MD5SUM})
+             message(FATAL_ERROR "Download of regressiontests failed. Expected MD5 of ${REGRESSIONTEST_MD5SUM} but download has ${COMPUTED_MD5SUM}")
+         endif()
+     endif()
  
      file(REMOVE_RECURSE "${REGRESSIONTEST_PATH}") #delete potential prior folder
      execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${REGRESSIONTEST_FILE}"