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")
"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
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()
#####################################################################
# 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()
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)
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)
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)
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)
"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
########################################################################
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)
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
# 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)
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
--- /dev/null
+#
+# 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()
`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).
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
/* 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. */
/* 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;
* 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
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
}
#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>
}
}
}
+ free(unique);
return nunique;
}
/* 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;
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;
* 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,
* 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 */
}
* 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
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 */
int np = 0;
char *copy0, *copy;
- copy0 = strdup(str);
+ copy0 = gmx_strdup(str);
copy = copy0;
ltrim(copy);
while (*copy != '\0')
}
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)
{
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");
double a, phi;
int i;
- t = strdup(s);
+ t = gmx_strdup(s);
trim(t);
cosine->n = 0;
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);
#include <stdio.h>
#include "typedefs.h"
#include "../math/gmxcomplex.h"
+#include "../timing/wallcycle.h"
#include "../timing/walltime_accounting.h"
#include "../legacyheaders/network.h"
/* 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,
* 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"
#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)
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))
{
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,
* 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
*/
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];
* /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"
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 */
* 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 "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"
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)
{
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");
}
}
}
* 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"
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);
namespace
{
-//! \addtogroup module_onlinehelp
+//! \internal \addtogroup module_onlinehelp
//! \{
struct t_sandr
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;
}
#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 ®istry = 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 ®istry = 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 ®istry = 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;
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 ®istry = 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);
}
/********************************************************************
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
*/
#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
{
* 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_;
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.
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;
}
}
-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];
}
}
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
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;
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_);
}
}
{
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;
}
}
}
else
{
- *pair = AnalysisNeighborhoodPair(previ_, testIndex_);
+ *pair = AnalysisNeighborhoodPair(previ_, testIndex_, prevr2_);
}
}
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
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;
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);
}
int closestPoint = -1;
MindistAction action(&closestPoint, &minDist2);
(void)pairSearch.searchNext(action);
- return AnalysisNeighborhoodPair(closestPoint, 0);
+ return AnalysisNeighborhoodPair(closestPoint, 0, minDist2);
}
AnalysisNeighborhoodPairSearch
/*
* 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.
*/
#include "selection.h"
+#include "gromacs/topology/topology.h"
+
#include "nbsearch.h"
#include "position.h"
#include "selelem.h"
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;
}
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
#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
{
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(); }
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.
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.
* \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"
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)
{
}
}
-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
*/
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_;
};
{
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.";
}
}
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:
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
* 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"
{
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
{
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.
"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}")
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}"